07 装饰者模式

1 星巴克咖啡订单项目

星巴克咖啡订单项目(咖啡馆):

  • 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
  • 调料:Milk、Soy(豆浆)、Chocolate
  • 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
  • 使用OO来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。
2 传统解决方案

方案一:

classDiagram class Espresso{ cost() void } class ShortBlack{ cost() void } class LongBlack{ cost() void } class Decaf{ cost() void } class EspressoANDMilk{ cost() void } class EspressoANDSoy{ cost() void } class EspressoANDChocolate{ cost() void } class Drink{ <<interface>> discription getDescription() cost() } Espresso --|> Drink : 继承 ShortBlack --|> Drink : 继承 LongBlack --|> Drink : 继承 Decaf --|> Drink : 继承 EspressoANDMilk --|> Drink : 继承 EspressoANDSoy --|> Drink : 继承 EspressoANDChocolate --|> Drink : 继承
  • Drink 是一个抽象类,表示饮料
  • description就是对咖啡的描述,比如咖啡的名字
  • cost()方法就是计算费用,Drink 类中做成一个抽象方法
  • Decaf 就是单品咖啡,继承Drink。并实现cost
  • EspressANDMilk就是单品咖啡+词料,这个组合很多
    这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案二:

classDiagram class Espresso{ cost() void } class ShortBlack{ cost() void } class LongBlack{ cost() void } class Decaf{ cost() void } class Drink{ <<interface>> discription milk soy chocolate getDescription() cost() hasMilk() setMilk() } Espresso --|> Drink : 继承 ShortBlack --|> Drink : 继承 LongBlack --|> Drink : 继承 Decaf --|> Drink : 继承
  • 方案2可以控制类的数量,不至于造成很多的类在增加或者删除调料种类时,代码的维护量很大
  • 考虑到用户可以添加多份调料时,可以将hasMilk 返回一个对应int
  • 考虑使用装饰者模式
3 装饰者模式

​ 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则。

4 装饰者模式原理
  1. 装饰者模式就像打包一个快递

    主体(被装饰者)比如:陶瓷、衣服(Component)

    包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)

  2. Component
    主体:比如类似前面的Drink

  3. ConcreteComponentConcreteComponent:具体的主体,比如前面的各个单品咖啡

  4. Decorator:装饰者,比如各调料
    在如图的ComponentConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类。

    classDiagram class Component{ <<abstract>> methodA() methodB() } class Decorator{ component Component methodA() methodB() } class ConcreteDecorator{ methodA() methodB() } class ConcreteComponent{ methodA() methodB() } Decorator --|> Component : 继承 ConcreteComponent --|> Component : 继承 ConcreteDecorator --|> Decorator : 继承 Decorator o-- Component : 聚合
5 装饰者模式解决星巴克咖啡问题
classDiagram class Espresso{ cost() void } class ShortBlack{ cost() void } class LongBlack{ cost() void } class Decaf{ cost() void } class Drink{ <<interface>> discription getDescription() cost() } Espresso --|> Coffee : 继承 ShortBlack --|> Coffee : 继承 LongBlack --|> Coffee : 继承 Decaf --|> Coffee : 继承 Coffee --|> Drink : 继承 class Decorator{ Drink drink getDescription() cost() } class Milk{ getDescription() cost() } class Soy{ getDescription() cost() } class Chocolate{ getDescription() cost() } Decorator --|> Drink : 继承 Decorator *-- Drink : 组合 Milk --|> Decorator : 继承 Soy --|> Decorator : 继承 Chocolate --|> Decorator : 继承
  • Drink类就是前面说的抽象类,component

  • shortBlack就单品咖菲

  • Decorator是一个装饰类,含有一个被装饰的对象(Drink drink)

  • Decorator 的cost方法进行一个费用的累加计算,递归的计算价格

  • Milk包含了LongBlack

  • 一份Chocolate包含了(Milk+LongBlack)

  • 一份Chocolate包含了(Chocolate+Milk+LongBlack)

  • 这样不管是什么形式的单品咖啡+调料组合,通过递归方式可以方便的组合和维护。

6 装饰者模式在JDK的应用
classDiagram class InputStream{ <<interface>> } FileInputStream ..|> InputStream : 实现 StringInputStream ..|> InputStream : 实现 ByteInputStream ..|> InputStream : 实现 FilterInputStream --|> InputStream : 继承 BufferInputStream --|> FilterInputStream : 继承 DataInputStream --|> FilterInputStream : 继承 LineNumberInputStream --|> FilterInputStream : 继承
DataInputStrcam dis = new DataInputStream(new FileInputStream("d:llabc.txt"));
System.out.println(dis.read));
dis.close();
  • InputStream是抽象类,类似我们前面讲的 Drink
  • FileInputStreamInputStream子类,类似我们前面的 DeCaf, LongBlack
  • FilterInputStreamInputStream子类:类似我们前面的Decorator修饰者
  • DataInputStreamFilterInputStream子类,具体的修饰者,类似前面的Milk, Soy
  • FilterInputStream类有protected volatile InputStream in;即含被装饰者
    分析得出在jdk的io体系中,就是使用装饰者模式
原文地址:https://www.cnblogs.com/nojacky/p/13922690.html