从一个需求入手
咖啡馆订单系统项目(咖啡馆):
- 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因 咖啡)
- 调料:Milk、Soy(豆浆)、Chocolate
- 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
- 使用 OO 的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。
方案 1-较差的方案
- Drink 是一个抽象类,表示饮料
- description就是描述,比如咖啡的名字等
- cost就是计算费用,是一个抽象方法
- Decaf 等等就是具体的单品咖啡,继承 Drink,并实现 cost 方法
- Espresso&&Milk 等等就是单品咖啡+各种调料的组合,这个会很多…
- 这种设计方式时,会有很多的类,并且当增加一个新的单品咖啡或者调料时,类的数量就会倍增(
类爆炸
)
方案 2-好点的方案
前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)=>同时违反 ocp原则
装饰者模式定义
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性
(递归)
,装饰者模式也体现了开闭原则(ocp) - 这里提到的动态的将新功能附加到对象和
ocp 原则
,在后面的应用实例上会以代码的形式体现,请各位老哥们注意体会。
用装饰者模式设计重新设计的方案
装饰者模式原理
- 装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服 (Component)
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator) - Component
主体:比如类似前面的 Drink - ConcreteComponent和Decorator ConcreteComponent:具体的主体, 比如前面的各个单品咖啡
- Decorator: 装饰者,比如各调料.
在如图的 Component 与 ConcreteComponent 之间,如果 ConcreteComponent 类很多,还可以设计一 个缓冲层,将共有的部分提取出来,抽象层一个类。
代码实现
Drink 老祖宗类:
//说明
//1. Drink 是表示饮品,是一个抽象类
abstract class Drink {
var description = ""
private var price = 0.0f
def setDescription(description: String): Unit = {
this.description = description
}
def getDescription(): String = {
description + " 价格: " + this.getPrice()
}
def getPrice(): Float = {
price
}
def setPrice(price: Float): Unit = {
this.price = price
}
//将计算成本的方法做成一个抽象方法cost
def cost(): Float
}
Coffee子类:
Coffee 继承 Drink 作为缓冲层, 可有可无, 为扩展以防万一.
//在Drink 和 单品咖啡,我做了一个缓冲层
//这里是为了扩展,针对当前项目可以不要
class Coffee extends Drink {
override def cost(): Float = {
super.getPrice()
}
}
Coffee种类: DeCaf
class DeCaf extends Coffee {
//使用主构造器
super.setDescription("DeCaf")
super.setPrice(3.0f)
}
Coffee种类: Espresso
//这个是单品咖啡,在装饰者设计模式中ConcreteComponent
class Espresso extends Coffee {
//使用主构造器
super.setDescription("Espresso")
super.setPrice(6.0f)
}
Coffee种类: LongBlack
class LongBlack extends Coffee {
//使用主构造器
super.setDescription("LongBlack")
super.setPrice(5.0f)
}
Coffee种类: ShortBlack
class ShortBlack extends Coffee {
//使用主构造器
super.setDescription("ShortBlack")
super.setPrice(4.0f)
}
装饰器类:
装饰器父类也必须继承自Drink, 因为要互相传递, 包裹.
注意: cost() 和 getDescription(), 一定要递归实现, 因为不知道包了多少层装饰器!
//这个就是Decorator装饰者
class Decorator extends Drink {
//obj就是被装饰的对象Drink
//obj可以是单品咖啡,也可以是单品咖啡+调料的组合
private var obj: Drink = null
def this(obj: Drink) {
this
this.obj = obj
}
//这里我们实现了cost,这里使用了递归方式
override def cost(): Float = {
super.getPrice() + obj.cost()
}
//获取信息时,也需要递归获取
override def getDescription(): String = {
super.getDescription() + "&&" + obj.getDescription()
}
}
Chocolate装饰器:
class Chocolate(obj: Drink) extends Decorator(obj) {
super.setDescription("Chocolate")
//一份巧克力3.0f
super.setPrice(3.0f)
}
Milk装饰器:
class Milk(obj: Drink) extends Decorator(obj) {
setDescription("Milk")
setPrice(2.0f)
}
Soy装饰器:
class Soy(obj: Drink) extends Decorator(obj) {
setDescription("Soy")
setPrice(1.5f)
}
main方法测试:
object CoffeeBar {
def main(args: Array[String]): Unit = {
println("咖啡bar..")
val order: Drink = new DeCaf //点DeCaf单品咖啡
println("order1 price:" + order.cost()) //3.0
println("order1 desc:" + order.getDescription())
println("------------------------------------------")
// 点一份LongBlack,并加入1份Milk 和 2份Chocolate
var order2: Drink = new LongBlack //5.0
order2 = new Milk(order2) //2.0
order2 = new Chocolate(order2) //3.0
order2 = new Chocolate(order2) //3.0
order2 = new NewMilk(order2)
println("order2 price:" + order2.cost());
println("order2 desc:" + order2.getDescription())
}
}
JDK内置的: java.io.FilterInputStream
也应用了装饰器模式:
注意是Filter, 不是File, 我自己就弄错了…