Entenda o "Padrão do decorador" com um exemplo do mundo real

167

Eu estava estudando o Padrão Decorador conforme documentado no GOF .

Por favor, ajude-me a entender o Padrão Decorador . Alguém poderia dar um exemplo de caso de uso de onde isso é útil no mundo real?

odiseh
fonte
8
Você pode encontrar aqui alguns exemplos do mundo real na API Java: stackoverflow.com/questions/1673841/…
BalusC:
Um artigo que mostra os benefícios do padrão decorador com exemplos simples: dzone.com/articles/is-inheritance-dead
nbilal

Respostas:

226

O padrão Decorator atinge um objetivo único de adicionar dinamicamente responsabilidades a qualquer objeto.

Considere um caso de uma pizzaria. Na pizzaria, eles venderão poucas variedades de pizza e também fornecerão coberturas no menu. Agora imagine uma situação em que se a pizzaria precisar fornecer preços para cada combinação de pizza e cobertura. Mesmo se houver quatro pizzas básicas e oito coberturas diferentes, o aplicativo ficaria louco, mantendo toda essa combinação concreta de pizzas e coberturas.

Aí vem o padrão do decorador.

De acordo com o padrão do decorador, você implementará coberturas à medida que decoradores e pizzas serão decoradas pelos decoradores dessas coberturas. Praticamente cada cliente deseja coberturas de seu desejo e o valor final da fatura será composto pelas pizzas básicas e coberturas encomendadas adicionalmente. Cada decorador de cobertura saberia sobre as pizzas que está decorando e seu preço. O método GetPrice () do objeto Cobertura retornaria o preço cumulativo da pizza e da cobertura.

EDITAR

Aqui está um exemplo de código da explicação acima.

public abstract class BasePizza
{
    protected double myPrice;

    public virtual double GetPrice()
    {
        return this.myPrice;
    }
}

public abstract class ToppingsDecorator : BasePizza
{
    protected BasePizza pizza;
    public ToppingsDecorator(BasePizza pizzaToDecorate)
    {
        this.pizza = pizzaToDecorate;
    }

    public override double GetPrice()
    {
        return (this.pizza.GetPrice() + this.myPrice);
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //Client-code
        Margherita pizza = new Margherita();
        Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());

        ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
        ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());

        MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());

        JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());

        Console.ReadLine();
    }
}

public class Margherita : BasePizza
{
    public Margherita()
    {
        this.myPrice = 6.99;
    }
}

public class Gourmet : BasePizza
{
    public Gourmet()
    {
        this.myPrice = 7.49;
    }
}

public class ExtraCheeseTopping : ToppingsDecorator
{
    public ExtraCheeseTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 0.99;
    }
}

public class MushroomTopping : ToppingsDecorator
{
    public MushroomTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

public class JalapenoTopping : ToppingsDecorator
{
    public JalapenoTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}
isso. __curious_geek
fonte
104
Não goste desse padrão nem um pouco. Talvez seja o exemplo. A principal questão que tenho em termos de OOD é que uma cobertura não é uma pizza . Pedir a cobertura pelo preço da pizza à qual ela é aplicada simplesmente não fica bem comigo. É um exemplo muito atencioso e detalhado, por isso não pretendo te bater por isso.
Tom W
39
@ TomW Acho que parte da questão é a nomeação. Todas as classes "Cobertura" devem se chamar "PizzaWith <Topping>". Por exemplo, "PizzaWithMushrooms".
21713 Josh Noe
2
Na minha opinião, os decoradores são melhor utilizados da maneira mais simples possível. Com isso, quero dizer o mínimo possível de "decoradores embrulhando decoradores". Portanto, talvez este exemplo não seja o mais adequado. Mas é bem completo, o que é legal.
thekingoftruth
17
De outra perspectiva, isso nem chega perto do "mundo real". No mundo real, você não deve recompilar toda vez que precisar adicionar uma nova cobertura no menu (ou alterar o preço). As coberturas são (geralmente) armazenadas no banco de dados e, portanto, tornam o exemplo acima inútil.
Stelios Adamantidis
4
^ Isso. Acho que é isso que me incomoda o tempo todo enquanto estudava esse padrão. Se eu fosse uma empresa de software e escrevesse um software de pizzaria, não gostaria de recompilar e reenviar todas as vezes. Gostaria de adicionar uma linha em uma tabela no back-end ou algo que atendesse facilmente aos requisitos deles. Bem dito, @Stelios Adamantidis. Eu acho que a maior força dos padrões seria modificar as classes de terceiros então.
Canucklesandwich
33

Este é um exemplo simples de adição de novo comportamento a um objeto existente dinamicamente, ou o padrão Decorator. Devido à natureza das linguagens dinâmicas, como o Javascript, esse padrão se torna parte da própria linguagem.

// Person object that we will be decorating with logging capability
var person = {
  name: "Foo",
  city: "Bar"
};

// Function that serves as a decorator and dynamically adds the log method to a given object
function MakeLoggable(object) {
  object.log = function(property) {
    console.log(this[property]);
  }
}

// Person is given the dynamic responsibility here
MakeLoggable(person);

// Using the newly added functionality
person.log('name');

Anurag
fonte
Simples e preciso! Ótimo exemplo!
Nagendra547 6/08/19
1
Não acho que o conceito de Padrão Decorador seja aplicável aqui. Na verdade, não é um padrão! Sim, você está adicionando um novo método em tempo de execução. E provavelmente dentro de um switchou de um simples if, você seria capaz de afirmar que este é um ótimo exemplo de adição dinâmica de comportamento a uma classe. Porém, precisamos de pelo menos duas classes para definir um decorador e objetos decorados nesse padrão.
Iman
1
@ Zich Eu entendo que não há decorador no meu exemplo, mas isso é facilmente corrigido adicionando uma função que serve como decorador. Mas há um objeto decorado no meu exemplo. O padrão diz em algum lugar que você precisa especificamente de duas classes ?
Anurag
18

Vale ressaltar que o modelo Java i / o é baseado no padrão decorador. As camadas deste leitor em cima desse leitor em cima de ... é um exemplo de decorador realmente real.

frankc
fonte
Existem outros exemplos em APIs públicas reais? Este é o único que eu conheço.
Josias Yoder
Parece que todas as funções de wrapper na natureza têm algum tipo de padrão de decorador, é isso que eu acho que é?
Harvey Lin
Bom exemplo !!
Nagendra547 6/08/19
8

Exemplo - Cenário - Digamos que você esteja escrevendo um módulo de criptografia. Essa criptografia pode criptografar o arquivo limpo usando o padrão de criptografia DES - Data. Da mesma forma, em um sistema, você pode ter a criptografia como padrão de criptografia AES - Advance. Além disso, você pode ter a combinação de criptografia - primeiro DES e depois AES. Ou você pode ter primeiro AES, depois DES.

Discussão - Como você vai lidar com essa situação? Você não pode continuar criando o objeto dessas combinações - por exemplo - AES e DES - total de 4 combinações. Portanto, você precisa ter 4 objetos individuais. Isso se tornará complexo à medida que o tipo de criptografia aumentar.

Solução - Continue construindo as combinações de pilhas, dependendo da necessidade - em tempo de execução. Outra vantagem dessa abordagem de pilha é que você pode desenrolá-la facilmente.

Aqui está a solução - em C ++.

Primeiro, você precisa de uma classe base - uma unidade fundamental da pilha. Você pode pensar como a base da pilha. Neste exemplo, é um arquivo claro. Vamos seguir sempre o polimorfismo. Faça primeiro uma classe de interface desta unidade fundamental. Dessa forma, você pode implementá-lo como desejar. Além disso, você não precisa pensar em dependência enquanto inclui esta unidade fundamental.

Aqui está a classe de interface -

class IclearData
{
public:

    virtual std::string getData() = 0;
    virtual ~IclearData() = 0;
};

IclearData::~IclearData()
{
    std::cout<<"Destructor called of IclearData"<<std::endl;
}

Agora, implemente essa classe de interface -

class clearData:public IclearData
{
private:

    std::string m_data;

    clearData();

    void setData(std::string data)
        {
            m_data = data;
        }

public:

    std::string getData()
    {
        return m_data;
    }

    clearData(std::string data)
    {
        setData(data);
    }

    ~clearData()
    {
        std::cout<<"Destructor of clear Data Invoked"<<std::endl;
    }

};

Agora, vamos criar uma classe abstrata do decorador - que pode ser estendida para criar qualquer tipo de sabor - aqui o sabor é do tipo de criptografia. Esta classe abstrata do decorador está relacionada à classe base. Assim, o decorador "é um" tipo de classe de interface. Portanto, você precisa usar a herança.

class encryptionDecorator: public IclearData
{

protected:
    IclearData *p_mclearData;

    encryptionDecorator()
    {
      std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
    }

public:

    std::string getData()
    {
        return p_mclearData->getData();
    }

    encryptionDecorator(IclearData *clearData)
    {
        p_mclearData = clearData;
    }

    virtual std::string showDecryptedData() = 0;

    virtual ~encryptionDecorator() = 0;

};

encryptionDecorator::~encryptionDecorator()
{
    std::cout<<"Encryption Decorator Destructor called"<<std::endl;
}

Agora, vamos fazer uma classe de decorador concreto - Tipo de criptografia - AES -

const std::string aesEncrypt = "AES Encrypted ";

class aes: public encryptionDecorator
{

private:

    std::string m_aesData;

    aes();

public:

    aes(IclearData *pClearData): m_aesData(aesEncrypt)
    {
        p_mclearData = pClearData;
        m_aesData.append(p_mclearData->getData());
    }

    std::string getData()
        {
            return m_aesData;
        }

    std::string showDecryptedData(void)
    {
        m_aesData.erase(0,m_aesData.length());
        return m_aesData;
    }

};

Agora, digamos que o tipo de decorador seja DES -

const std :: string desEncrypt = "DES criptografado";

class des: public encryptionDecorator
{

private:

    std::string m_desData;

    des();

public:

    des(IclearData *pClearData): m_desData(desEncrypt)
    {
        p_mclearData = pClearData;
        m_desData.append(p_mclearData->getData());
    }

    std::string getData(void)
        {
            return m_desData;
        }

    std::string showDecryptedData(void)
    {
        m_desData.erase(0,desEncrypt.length());
        return m_desData;
    }

};

Vamos criar um código de cliente para usar essa classe decoradora -

int main()
{
    IclearData *pData = new clearData("HELLO_CLEAR_DATA");

    std::cout<<pData->getData()<<std::endl;


    encryptionDecorator *pAesData = new aes(pData);

    std::cout<<pAesData->getData()<<std::endl;

    encryptionDecorator *pDesData = new des(pAesData);

    std::cout<<pDesData->getData()<<std::endl;

    /** unwind the decorator stack ***/
    std::cout<<pDesData->showDecryptedData()<<std::endl;

    delete pDesData;
    delete pAesData;
    delete pData;

    return 0;
}

Você verá os seguintes resultados -

HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
DES Encrypted AES Encrypted HELLO_CLEAR_DATA
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Destructor called
Destructor called of IclearData
Encryption Decorator Destructor called
Destructor called of IclearData
Destructor of clear Data Invoked
Destructor called of IclearData

Aqui está o diagrama UML - Representação de classe dele. No caso, você deseja pular o código e se concentrar no aspecto do design.

insira a descrição da imagem aqui

hábil
fonte
1
não é o exemplo mais adequado strategy pattern?
exexzian 8/08/19
@exexzian Sim, meus alunos sugerem consistentemente uma lista de estratégias para esse tipo de problema e parece a solução mais limpa para mim também.
Josiah Yoder
Não, com o padrão de estratégia, você não pode combinar os métodos de criptografia. Antes disso, você teria que criar uma classe de estratégia para todas as combinações possíveis.
21419 Deetz
4

O padrão Decorator ajuda você a alterar ou configurar uma funcionalidade do seu objeto encadeando com outras subclasses semelhantes desse objeto.

O melhor exemplo seria as classes InputStream e OutputStream no pacote java.io

    File file=new File("target","test.txt");
    FileOutputStream fos=new FileOutputStream(file);
    BufferedOutputStream bos=new BufferedOutputStream(fos);
    ObjectOutputStream oos=new ObjectOutputStream(bos);


    oos.write(5);
    oos.writeBoolean(true);
    oos.writeBytes("decorator pattern was here.");


//... then close the streams of course.
huseyin
fonte
Nesse caso, a cadeia de chamada começa em ObjectOutputStream, depois sobe até a classe File, depois a classe File retorna o valor, as outras três subclasses as adicionam e, finalmente, o valor do método ObjectOutputStream a retorna. está certo?
amigos estão dizendo sobre harvey
3

O que é o Decorator Design Pattern em Java.

A definição formal do padrão Decorator do livro GoF (Design Patterns: Elements of Reusable Oriented Object Oriented, 1995, Pearson Education, Inc. Publishing como Pearson Addison Wesley) diz que você pode,

"Anexe responsabilidades adicionais a um objeto dinamicamente. Os decoradores oferecem uma alternativa flexível à subclasse para ampliar a funcionalidade".

Digamos que temos uma Pizza e queremos decorá-la com coberturas como frango Masala, cebola e queijo mussarela. Vamos ver como implementá-lo em Java ...

Programa para demonstrar como implementar o Decorator Design Pattern em Java.

Pizza.java:

<!-- language-all: lang-html -->

package com.hubberspot.designpattern.structural.decorator;

public class Pizza {

public Pizza() {

}

public String description(){
    return "Pizza";
}

}



package com.hubberspot.designpattern.structural.decorator;

public abstract class PizzaToppings extends Pizza {

public abstract String description();

}

package com.hubberspot.designpattern.structural.decorator;

public class ChickenMasala extends PizzaToppings {

private Pizza pizza;

public ChickenMasala(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + " with chicken masala, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class MozzarellaCheese extends PizzaToppings {

private Pizza pizza;

public MozzarellaCheese(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "and mozzarella cheese.";
}
}



package com.hubberspot.designpattern.structural.decorator;

public class Onion extends PizzaToppings {

private Pizza pizza;

public Onion(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "onions, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class TestDecorator {

public static void main(String[] args) {

    Pizza pizza = new Pizza();

    pizza = new ChickenMasala(pizza);
    pizza = new Onion(pizza);
    pizza = new MozzarellaCheese(pizza);

    System.out.println("You're getting " + pizza.description());

}

}
Jonty
fonte
3

Eu usei extensivamente o padrão Decorator no meu trabalho. Fiz um post no meu blog sobre como usá-lo com o log.

Ismael
fonte
Não gosto que você jogue um link como resposta. Mas o artigo do seu blog é tão útil que eu apenas tive que votar :). Agora eu realmente entendo isso. Todo mundo está vindo com pizza, e você com um exemplo perfeito.
Niklas Raab
2

O padrão do decorador permite adicionar dinamicamente o comportamento aos objetos.

Vamos dar um exemplo em que você precisa criar um aplicativo que calcule o preço de diferentes tipos de hambúrgueres. Você precisa lidar com diferentes variações de hambúrgueres, como "grande" ou "com queijo", cada qual com um preço em relação ao hambúrguer básico. Por exemplo, adicione US $ 10 por hambúrguer com queijo, US $ 15 a mais por hambúrguer grande, etc.

Nesse caso, você pode ser tentado a criar subclasses para lidar com elas. Podemos expressar isso em Ruby como:

class Burger
  def price
    50
  end
end

class BurgerWithCheese < Burger
  def price
    super + 15
  end
end

No exemplo acima, a classe BurgerWithCheese herda do Burger e substitui o método price para adicionar US $ 15 ao preço definido na superclasse. Você também criaria uma classe LargeBurger e definiria o preço em relação ao Burger. Mas você também precisa definir uma nova classe para a combinação de "grande" e "com queijo".

Agora, o que acontece se precisarmos servir "hambúrguer com batatas fritas"? Já temos 4 classes para lidar com essas combinações e precisaremos adicionar mais 4 para lidar com todas as combinações das 3 propriedades - "grande", "com queijo" e "com batatas fritas". Precisamos de 8 aulas agora. Adicione outra propriedade e precisaremos de 16. Isso aumentará como 2 ^ n.

Em vez disso, vamos tentar definir um BurgerDecorator que aceite um objeto Burger:

class BurgerDecorator
  def initialize(burger)
    self.burger = burger
  end
end

class BurgerWithCheese < BurgerDecorator
  def price
    self.burger.price + 15
  end
end

burger = Burger.new
cheese_burger = BurgerWithCheese.new(burger)
cheese_burger.price   # => 65

No exemplo acima, criamos uma classe BurgerDecorator, da qual a classe BurgerWithCheese herda. Também podemos representar a variação "grande" criando a classe LargeBurger. Agora poderíamos definir um hambúrguer grande com queijo em tempo de execução como:

b = LargeBurger.new(cheese_burger)
b.price  # => 50 + 15 + 20 = 85

Lembre-se de que o uso da herança para adicionar a variação "com batata frita" envolveria a adição de mais 4 subclasses? Com os decoradores, criaríamos apenas uma nova classe, BurgerWithFries, para lidar com a nova variação e lidar com isso em tempo de execução. Cada nova propriedade precisaria de apenas mais decorador para cobrir todas as permutações.

PS. Esta é a versão curta de um artigo que escrevi sobre o uso do Decorator Pattern em Ruby , que você pode ler se desejar descobrir exemplos mais detalhados.

Nithin
fonte
2

Decorador:

  1. Adicionar comportamento ao objeto em tempo de execução . A herança é a chave para alcançar essa funcionalidade, que é a vantagem e a desvantagem desse padrão.
  2. Melhora o comportamento da interface.
  3. O Decorator pode ser visto como um composto degenerado com apenas um componente. No entanto, um Decorator adiciona responsabilidades adicionais - não se destina à agregação de objetos.
  4. A classe Decorator declara um relacionamento de composição com a interface LCD (denominador de classe mais baixa) e esse membro de dados é inicializado em seu construtor.
  5. O Decorator foi desenvolvido para permitir adicionar responsabilidades aos objetos sem subclassificar

Consulte o artigo de criação de fontes para obter mais detalhes.

Decorador (Resumo) : é uma classe / interface abstrata que implementa a interface do componente. Ele contém interface de componentes. Na ausência dessa classe, você precisa de muitas subclasses de ConcreteDecorators para diferentes combinações. A composição do componente reduz subclasses desnecessárias.

Exemplo de JDK:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
        char c = (char)bis.read();
        System.out.println("Char: "+c);;
}

Dê uma olhada na pergunta SE abaixo para obter exemplos de diagrama e código UML.

Padrão Decorador para IO

Artigos úteis:

journaldev

wikipedia

Exemplo real de palavras do padrão Decorator: VendingMachineDecorator foi explicado @

Quando usar o padrão do decorador?

Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();

beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();

No exemplo acima, chá ou café (bebidas) foi decorado com açúcar e limão.

Ravindra babu
fonte
2

O padrão Decorator atinge um objetivo único de adicionar dinamicamente responsabilidades a qualquer objeto .

O modelo de E / S Java é baseado no padrão decorador.

Java IO como padrão de decorador

ChandraBhan Singh
fonte
1

Há um exemplo na Wikipedia sobre a decoração de uma janela com barra de rolagem:

http://en.wikipedia.org/wiki/Decorator_pattern

Aqui está outro exemplo muito "do mundo real" de "Membro da equipe, líder da equipe e gerente", que ilustra que o padrão do decorador é insubstituível com uma herança simples:

https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/

gm2008
fonte
Esse link do Zishan Bilal é ótimo - o melhor exemplo que eu já vi #
stonedauwg
1

Algum tempo atrás, refatorei uma base de código para usar o padrão Decorator, então tentarei explicar o caso de uso.

Vamos supor que temos um conjunto de serviços e, com base em se o usuário adquiriu a licença de um serviço específico, precisamos iniciar o serviço.

Todos os serviços têm uma interface comum

interface Service {
  String serviceId();
  void init() throws Exception;
  void start() throws Exception;
  void stop() throws Exception;
}

Pré-refatoração

abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId, LicenseManager licenseManager) {
    // assign instance variables
  }

  @Override
  public void init() throws Exception {
    if (!licenseManager.isLicenseValid(serviceId)) {
       throw new Exception("License not valid for service");
    }
    // Service initialization logic
  }
}

Se você observar atentamente, ServiceSupportdepende LicenseManager. Mas por que deveria depender LicenseManager? E se precisássemos de um serviço em segundo plano que não precise verificar as informações da licença. Na situação atual, teremos de alguma forma treinar LicenseManagerpara retornartrue aos serviços em segundo plano. Essa abordagem não me pareceu bem. De acordo com mim, a verificação da licença e outras lógicas eram ortogonais entre si.

Então o Decorator Pattern vem em socorro e aqui começa a refatoração com TDD.

Pós-refatoração

class LicensedService implements Service {
  private Service service;
  public LicensedService(LicenseManager licenseManager, Service service) {
    this.service = service;
  }

  @Override
  public void init() {
    if (!licenseManager.isLicenseValid(service.serviceId())) {
      throw new Exception("License is invalid for service " + service.serviceId());
    }
    // Delegate init to decorated service
    service.init();
  }

  // override other methods according to requirement
}

// Not concerned with licensing any more :)
abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId) {
    // assign variables
  }

  @Override
  public void init() {
    // Service initialization logic
  }
}

// The services which need license protection can be decorated with a Licensed service
Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
// Services which don't need license can be created without one and there is no need to pass license related information
Service aBackgroundService = new BackgroundService1("BG-1");

Aprendizado

  • A coesão do código ficou melhor
  • O teste de unidade ficou mais fácil, pois não é necessário zombar do licenciamento ao testar o ServiceSupport
  • Não é necessário ignorar o licenciamento por nenhuma verificação especial de serviços em segundo plano
  • Divisão adequada de responsabilidades
Narendra Pathai
fonte
1

Vamos dar um exemplo do PubG. As espingardas de assalto funcionam melhor com zoom 4x e enquanto estivermos nele, também precisaremos de compensador e supressor. Reduzirá o recuo e o som do disparo, bem como o eco. Precisamos implementar esse recurso, onde permitiremos que os jogadores comprem sua arma favorita e seus acessórios. Os jogadores podem comprar a arma ou parte do acessório ou todo o acessório e eles serão cobrados de acordo.

Vamos ver como o padrão do decorador é aplicado aqui:

Suponha que alguém queira comprar o SCAR-L com todos os três acessórios mencionados acima.

  1. Pegue um objeto de SCAR-L
  2. Decore (ou adicione) o objeto SCAR-L com zoom de 4x
  3. Decore o SCAR-L com objeto supressor
  4. Decore o SCAR-L com objeto compressor
  5. Chame o método de custo e permita que cada objeto delegue para aumentar o custo usando o método de custo de acessórios

Isso levará a um diagrama de classes como este:

Padrão de decorador no trabalho

Agora, podemos ter aulas como esta:

public abstract class Gun {     
    private Double cost;    
    public Double getCost() {           
        return cost;        
       }    
    }

public abstract class GunAccessories extends Gun {  }

public class Scarl extends Gun {    
    public Scarl() {            
        cost = 100;
        }   
     }

public class Suppressor extends GunAccessories {        
    Gun gun;        
    public Suppressor(Gun gun) {            
    cost = 5;           
    this.gun = gun;     
    }               
    public double getCost(){            
        return cost + gun.getCost();
    }
}

public class GunShop{   
    public static void main(String args[]){         
    Gun scarl = new Scarl();                
    scarl = new Supressor(scarl);
    System.out.println("Price is "+scarl.getCost());
    }      
}

Da mesma forma, também podemos adicionar outros acessórios e decorar a nossa arma.

Referência:

https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/

HV
fonte
0

Padrão de design do decorador : esse padrão ajuda a modificar as características de um objeto em tempo de execução. Ele fornece sabores diferentes para um objeto e oferece flexibilidade para escolher quais ingredientes queremos usar nesse sabor.

Exemplo da vida real: digamos que você tenha um assento na cabine principal em um voo. Agora você pode escolher várias comodidades com o assento. Cada comodidade tem seu próprio custo associado. Agora, se um usuário escolher Wifi e comida premium, ele será cobrado por assento + wifi + comida premium.

insira a descrição da imagem aqui

Nesse caso, o padrão de design do decorador pode realmente nos ajudar. Visite o link acima para entender mais sobre o padrão do decorador e a implementação de um exemplo da vida real.

Ajit Singh
fonte