用一个真实世界的例子来理解“装饰模式”

我正在研究GOF中logging的装饰者模式

请帮我理解装饰模式 。 有人可以给出一个在现实世界中有用的用例吗?

装饰模式实现了向任何对象dynamic添加责任的单一目标。

考虑比萨饼店的情况。 在披萨店,他们将出售几个比萨饼品种,他们还会在菜单中提供配料。 现在想象一下,如果比萨饼店必须提供比萨和打顶的每种组合的价格。 即使有四个基本比萨饼和8个不同的配料,应用程序将疯狂维护所有这些比萨饼和配料的具体组合。

这里是装饰者模式。

根据装饰者模式,你将实现浇头作为装饰和比萨饼将由装饰的装饰。 实际上,每个顾客都会想要自己的愿望,最后的账单金额将由基地比萨饼和额外订购的配料组成。 每个顶级装饰者都会知道它正在装修的比萨饼和价格。 Topping对象的GetPrice()方法将返回比萨和顶部的累计价格。

编辑

这是上面解释的代码示例。

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; } } 

这是一个dynamic添加新行为到现有对象或Decorator模式的简单示例。 由于dynamic语言(如Javascript)的性质,这种模式成为语言本身的一部分。

 // create a message object var message = { text: "Lorem ipsum dolor sit amet, consectetur adipisicing elit..." }; // add logging behavior to the message object dynamically message.log = function() { console.log(this.text); }; // use the newly added behavior to log text ​message.log();​ // Loren ipsum...​​​​​​​​​​​​​​​ 

值得注意的是,Java I / O模型是基于装饰器模式的。 这个阅读器上层的阅读器层层叠叠,是一个非常真实的装饰器例子。

示例 – 场景 – 假设您正在编写encryption模块。 此encryption可以使用DES – 数据encryption标准来encryption清除文件。 同样,在一个系统中,您可以将encryption作为AES – 高级encryption标准。 此外,你可以有encryption的组合 – 第一DES,然后AES。 或者你可以有第一个AES,然后是DES。

讨论 – 你将如何迎合这种情况? 您不能继续创build这种组合的对象 – 例如 – AES和DES – 共有4种组合。 因此,你需要有4个单独的对象随着encryptiontypes的增加,这将变得复杂。

解决scheme – 在运行时根据需要build立堆栈 – 组合。 这种堆栈方式的另一个优点是可以轻松地将其展开。

这里是C ++的解决scheme。

首先,你需要一个基类 – 堆栈的基本单元。 你可以把它当作堆栈的基础。 在这个例子中,它是明确的文件。 让我们遵循总是多态。 首先制作这个基本单元的接口类。 这样,你可以按照你的意愿来实现它。 另外,包含这个基本单位的时候不需要考虑依赖性。

这里是接口类 –

 class IclearData { public: virtual std::string getData() = 0; virtual ~IclearData() = 0; }; IclearData::~IclearData() { std::cout<<"Destructor called of IclearData"<<std::endl; } 

现在,实现这个接口类 –

 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; } }; 

现在,我们来创build一个装饰器抽象类 – 可以扩展来创build任何types的风味 – 这里的风格是encryptiontypes。 这个装饰器抽象类与基类有关。 因此,装饰器“是一种”接口类。 因此,你需要使用inheritance。

 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; } 

现在,让我们来制作一个具体的装饰器类 – encryptiontypes – 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; } }; 

现在,让我们说装饰types是DES –

const std :: string desEncrypt =“DES Encrypted”;

 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; } }; 

让我们做一个客户端代码来使用这个装饰类 –

 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; } 

你会看到以下结果 –

 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 

这是UML图 – 它的类表示。 如果您想跳过代码并专注于devise方面。

在这里输入图像说明

Java中的装饰器devise模式是什么?

从GoF书(Design Patterns:Elements of Reusable Object-Oriented Software,1995,Pearson Education,Inc. Publishing as Pearson Addison Wesley出版)中,Decorator模式的正式定义说,

“将其他职责dynamic地附加到对象上,装饰器为扩展function提供了子类化的灵活替代scheme。”

比方说,我们有一个披萨,我们要装饰,如鸡肉Masala,洋葱和马苏里拉奶酪配料。 我们来看看如何在Java中实现它…

程序演示如何在Java中实现装饰devise模式。

  • 有关详情,请访问: http : //www.hubberspot.com/2013/06/decorator-design-pattern-in-java.html#sthash.zKj0xLrR.dpuf

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()); } } 

装饰者模式可以让你dynamic地将行为添加到对象。

我们举一个例子,您需要构build一个计算不同种类汉堡包的价格的应用程序。 你需要处理不同的汉堡包,比如“大”或者“带芝士”,每个汉堡都有相对于基本汉堡的价格。 比如加芝士汉堡包10元,大汉堡加15元等。

在这种情况下,您可能会试图创build子类来处理这些。 我们可以在Ruby中表示为:

 class Burger def price 50 end end class BurgerWithCheese < Burger def price super + 15 end end 

在上面的例子中,BurgerWithCheese类inheritance了Burger,并且重写了price方法,在super类中定义的价格上增加了$ 15。 您还将创build一个LargeBurger类并定义相对于Burger的价格。 但是,您还需要为“大”和“与奶酪”的组合定义一个新class级。

现在,如果我们需要为“汉堡配炸薯条”服务呢? 我们已经有4个class级来处理这些组合,我们还需要增加4个来处理3个属性的所有组合 – “大”,“用奶酪”和“用薯条”。 我们现在需要8个class。 添加另一个属性,我们将需要16.这将增长为2 ^ n。

相反,让我们试着定义一个接受Burger对象的BurgerDecorator:

 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 

在上面的例子中,我们创build了BurgerWithCheese类inheritance的BurgerDecorator类。 我们也可以通过创buildLargeBurger类来表示“大”的变化。 现在我们可以在运行时定义一个带有奶酪的大汉堡:

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

还记得如何使用inheritance来添加“与炸薯条”变异会涉及添加4个更多的子类? 使用装饰器,我们只需创build一个新类BurgerWithFries来处理新的变体,并在运行时处理这个变体。 每个新的属性都需要更多的装饰器来覆盖所有的排列。

PS。 这是我写的关于在Ruby中使用Decorator模式的文章的简短版本,如果您想了解更详细的示例,可以阅读这篇文章。

装饰devise模式 :这种模式有助于在运行时修改对象的特征。 它为对象提供不同的口味,并且灵活地select我们想要在这种口味中使用的成分。

真实生活的例子:假设你在飞机上有一个主舱位。 现在你可以select座位上的多种设施。 每个市容都有它自己的成本。 现在,如果用户selectWifi和高级食品,他/她将被收取座位+ wifi +高级食品。

在这里输入图像说明

在这种情况下,装饰devise模式可以真正帮助我们。 访问上面的链接了解更多关于装饰模式和一个真实生活的例子的实现。

维基百科上有一个关于使用滚动条来装饰窗口的例子:

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

这里是另一个非常“现实世界”的“团队成员,团队领导和经理”的例子,它说明了装饰模式是简单的inheritance不可替代的:

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

我在工作中广泛使用了Decorator模式。 我在博客上发表了一篇关于如何在日志中使用它的文章。

修饰器模式可以帮助您通过链接此对象的其他类似的子类来更改或configuration对象的function。

最好的例子是java.io包中的InputStream和OutputStream类

  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. 

装饰:

  1. 在运行时将行为添加到对象 。 inheritance是实现这种function的关键,这是这种模式的优点和缺点。
  2. 它增强了界面的行为
  3. 装饰者可以被看作只有一个组件的退化组合 。 然而,装饰者增加了额外的责任 – 它不适用于对象聚合。
  4. Decorator类声明一个到LCD(最低类分母)接口的合成关系,并且这个数据成员在其构造函数中被初始化。
  5. 装饰器的目的是让你没有子分类的对象增加责任

有关更多详细信息,请参阅源文章。

Decorator(抽象) :它是一个抽象的类/接口,它实现了组件接口。 它包含Component接口。 在没有这个类的情况下,你需要很多的ConcreteDecorators的子类来进行不同的组合。 组件的组成减less了不必要的子类别。

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);; } 

下面看看SE问题的UML图和代码示例。

IO的装饰模式

有用的文章:

journaldev

维基百科

Decorator模式的真实例子:VendingMachineDecorator已被解释@

何时使用装饰模式?

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

在上面的例子中,茶或咖啡(饮料)已被糖和柠檬装饰。