一、装饰者模式
1.1 案例分析
描述:
星巴克咖啡订单项目(咖啡馆):
- 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无音咖啡)
- 调料:Milk、Soy(豆浆)、Chocolate
要求:
在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
费用:
在计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。
1.1.1 解析—方案1
方案1如下图所示:
(1)将咖啡抽象为一个类:Drink
(2)使用单品咖啡继承该抽象类
(3)使用单品咖啡+调料来组合继承该抽象类
1.1.1.1 方案1问题分析
- Drink是一个抽象类,表示饮料
- description就是对咖啡的描述,比如咖啡的名字
- cost()方法就是计算费用,Drink类中做成一个抽象方法
- Decaf就是单品咖啡,继承Drink,并实现cost
- Espress&&Milk就是单品咖啡+调料,这个组合很多
问题:
- 这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会暴增,就会出现类爆炸。
1.1.2 解析—方案2
方案1因为单品咖啡+调料组合会造成类的倍增。因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。
方案2如下图所示:
(1)将调料写在抽象类中,避免类过多
(2)实现has方法,来确定是否存在某些调料,set方法时,可用Boolean型,表示是否要添加相应的调料
(3)当某些调料有多份时,可以用int型
1.1.2 方案2问题分析
- 方案2可以控制类的数量,不至于造成很多的类
- 在增加或者删除调料种类时,代码的维护量很大
- 考虑到用户可以添加多份调料时,可以将hasMilk返回一个对应int
- 考虑使用装饰者模式
1.2 装饰者模式定义
装饰者模式:动态的将新功能附加到对象上。
在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则。
1.3 装饰者模式(Decorator)原理
- Component:主体,类似于前面的Drink
- ConcreteComponent:具体的主体,比如前面的各个单品咖啡
- Decorator:装饰者,比如各调料
1.4 装饰者模式解决咖啡订单问题
- Drink类就是前面说的抽象类,也就是Component
- ShortBlack就是单品咖啡
- Decorator是一个装饰类,含有一个被装饰的对象(Drink obj)
- Decorator的cost方法 进行一个费用的叠加计算,递归的计算价格
1.5 装饰者模式图解
- LongBlack是一个被装饰对象
- Milk包含了LongBlack,用来装饰LongBlack
- 将Mink & LongBlack看着一个被装饰对象
- Chocolate包含了Mink & LongBlack,用来装饰Mink & LongBlack
1.6 代码实例
1.6.1 Drink抽象类
1 | public abstract class Drink { |
1.6.2 Coffee类:继承Drink
1 | public class Coffee extends Drink { |
1.6.3 Espresso类:继承Coffee
1 | public class Espresso extends Coffee { |
1.6.4 LongBlack类:继承Coffee
1 | public class LongBlack extends Coffee { |
1.6.5 ShortBlack类:继承Coffee
1 | public class ShortBlack extends Coffee { |
1.6.6 Deorator类:装饰类:继承Drink,并且组合Drink
1 | public class Decorator extends Drink { |
1.6.7 Chocolate类:继承Decorator
1 | //具体的Decorator,这里就是调味品 |
1.6.8 Milk类:继承Decorator
1 | public class Milk extends Decorator { |
1.6.9 Soy类:继承Decorator
1 | public class Soy extends Decorator { |
1.6.10 CoffeeBar类:客户端类
1 | public class CoffeeBar { |
运行结果:
1
2
3
4
5
6
7
8 费用1=6.0
描述=LongBlack
order 加入一份牛奶 费用= 8.0
order 加入一份牛奶 描述=牛奶 2.0&&LongBlack
order 加入一份牛奶 加入一份巧克力 费用= 11.0
order 加入一份牛奶 加入一份巧克力 描述= 巧克力 3.0&&牛奶 2.0&&LongBlack
order 加入一份牛奶 加入2份巧克力 费用= 14.0
order 加入一份牛奶 加入2份巧克力 描述= 巧克力 3.0&& 巧克力 3.0&&牛奶 2.0&&LongBlack分析:使用装饰者模式的好处?
- 比如想加入一个Decaf
1.6.11 Decaf类:继承Coffee
1 | public class Decaf extends Coffee { |
1.6.12 CoffeeBar类:客户端类
1 | public class CoffeeBar { |
结果:
1
2
3
4
5
6
7
8
9
10
11
12
13 费用1=6.0
描述=LongBlack
order 加入一份牛奶 费用= 8.0
order 加入一份牛奶 描述=牛奶 2.0&&LongBlack
order 加入一份牛奶 加入一份巧克力 费用= 11.0
order 加入一份牛奶 加入一份巧克力 描述= 巧克力 3.0&&牛奶 2.0&&LongBlack
order 加入一份牛奶 加入2份巧克力 费用= 14.0
order 加入一份牛奶 加入2份巧克力 描述= 巧克力 3.0&& 巧克力 3.0&&牛奶 2.0&&LongBlack
===================================
费用1=1.0
描述=Decaf
order 加入一份牛奶 费用= 4.0
order 加入一份牛奶 描述= 巧克力 3.0&&Decaf
1.7 装饰者模式在JDK应用的源码分析
以java.io流为例进行分析,在看InputStream的实现类时,主要看其中的IO包下的类
1.7.1 源码客户端
1 | import java.io.DataInputStream; |
1.7.2 InputStream类:抽象类
1 | public abstract class InputStream implements Closeable { |
1.7.3 FileInputStream源码:继承InputStream
1 | public |
1.7.4 FilterInputStream类:继承InputStream,并且组合InputStream
1 | public |
1.7.5 DataInputStream源码:继承FilterInputStream
1 | public class DataInputStream extends FilterInputStream implements DataInput { |
结合源码分析:
- InputStream是抽象类,类似于我们前面的Drink
- FileInputStream是InputStream子类,类似于我们前面的Decaf、LongBlack
- FilterInputStream是InputStream子类,类似于我们前面的Decorator修饰者,含有 protected volatile InputStream in;
- DataInputStrea是FilterInputStream子类,具体的修饰者,类似前面的Milk,Soy等
- 分析得出,在jdk的io体系中,就是使用的装饰者模式