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?
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?
Respostas:
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.
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;
}
}
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');
switch
ou 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.
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.
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.
strategy pattern
?
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.
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());
}
}
Eu usei extensivamente o padrão Decorator no meu trabalho. Fiz um post no meu blog sobre como usá-lo com o log.
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.
Decorador:
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.
Artigos úteis:
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.
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.
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/
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;
}
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, ServiceSupport
depende 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 LicenseManager
para 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.
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");
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.
Isso levará a um diagrama de classes como este:
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/
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.
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.