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 装饰者模式原理
-
装饰者模式就像打包一个快递
主体(被装饰者)比如:陶瓷、衣服(Component)
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
-
Component
主体:比如类似前面的Drink -
ConcreteComponent
和ConcreteComponent
:具体的主体,比如前面的各个单品咖啡 -
Decorator
:装饰者,比如各调料
在如图的Component
与ConcreteComponent
之间,如果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
FileInputStream
是InputStream
子类,类似我们前面的DeCaf
,LongBlack
FilterInputStream
是InputStream
子类:类似我们前面的Decorator
修饰者DataInputStream
是FilterInputStream
子类,具体的修饰者,类似前面的Milk
,Soy
等FilterInputStream
类有protected volatile InputStream in
;即含被装饰者
分析得出在jdk的io体系中,就是使用装饰者模式