看一个项目需求
咖啡馆订单系统项目(咖啡馆):
1)咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
2)调料:Milk、Soy(豆浆)、Chocolate
3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
4)使用**OO(面向对象)**的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。
方案一:
方案1-小结和分析
1)Drink 是一个抽象类,表示饮料
2)description就是描述,比如咖啡的名字等
3)cost就是计算费用,是一个抽象方法
4)Decaf 等等就是具体的单品咖啡,继承Drink,并实现cost方法
5)Espresso&&Milk 等等就是单品咖啡+各种调料的组合,这个会很多…
6)这种设计方式时,会有很多的类,并且当增加一个新的单品咖啡或者调料时,类的数量就会倍增(类爆炸)
方案二:
将调料内置到Drink 类中,这样就不会造成类的数量过多。
分析:
方案2可以控制类的数量,不至于造成过多的类。
在增删调料种类时,代码维护量仍然很大。
装饰者模式
装饰者模式原理
1)装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服 (Component)
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
2)Component
主体:比如类似前面的Drink
3)ConcreteComponent和DecoratorConcreteComponent:具体的主体,比如前面的各个单品咖啡Decorator: 装饰者,比如各调料.
4)在如图的Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。
装饰者模式定义
1)装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)(功能扩展开放,代码(已有的)修改关闭)
2)这里提到的动态的将新功能附加到对象和ocp原则,在后面的应用实例上会以代码的形式体现,请同学们注意体会。
通过装饰者模式设计的方案
Drink 设定标准,设置价格,获取价格等操作
代码结构:
Coffee,DeCaf,Espresso,LongBlack,ShortBlack 都继承自Drink。
Chocolate,Milk,NewMilk,Soy 都是继承自 Decorator。
Decorator 也继承自 Drink。
代码:
主程序:
import Decorator.coffeebar.mycoffee.{DeCaf, LongBlack}
import Decorator.coffeebar.mydecorator.{Chocolate, Milk, NewMilk}
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("order3 price:"+order2.cost()); //
println("order3 desc:"+order2.getDescription())
}
}
我觉得这里可以换成这样更容易理解,每一次都将上一个对象给包装了起来。
这里var 可以看到是灰色的。但是如果是上面代码中的写法,order2 需要时var 类型的
输出结果:
Drink类:
package Decorator.coffeebar
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
}
Decorator:
package Decorator.coffeebar.mydecorator
import Decorator.coffeebar.Drink
//这个就是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()
}
}
Decorator 继承自 Drink。里面提供了辅助构造器可以传入 Drink 对象。
Decorator 的子类在创建实例的时候就可以传入 Drink 的子类实例
而子类中会设置相应的方法,在被调用时就会自动执行
咖啡种类:
Coffee:
import Decorator.coffeebar.Drink
//在Drink 和 单品咖啡,做了一个缓冲层
//这里是为了扩展,针对当前项目可以不要
class Coffee extends Drink{
override def cost(): Float = {
super.getPrice()
}
}
DeCaf:
//在Drink 和 单品咖啡,做了一个缓冲层
//这里是为了扩展,针对当前项目可以不要
class DeCaf extends Coffee {
//使用主构造器
super.setDescription("DeCaf")
super.setPrice(3.0f)
}
Espresso:
//这个是单品咖啡,在装饰者设计模式中ConcreteComponent
class Espresso extends Coffee {
//使用主构造器
super.setDescription("Espresso")
super.setPrice(6.0f)
}
LongBlack:
class LongBlack extends Coffee {
//使用主构造器
super.setDescription("LongBlack")
super.setPrice(5.0f)
}
ShortBlack:
class ShortBlack extends Coffee {
//使用主构造器
super.setDescription("ShortBlack")
super.setPrice(4.0f)
}
调料种类:
Chocolate:
import Decorator.coffeebar.Drink
class Chocolate(obj: Drink) extends Decorator(obj) {
super.setDescription("Chocolate")
//一份巧克力3.0f
super.setPrice(3.0f)
}
Milk:
import Decorator.coffeebar.Drink
class Milk(obj: Drink) extends Decorator(obj) {
setDescription("Milk")
setPrice(2.0f)
}
NewMilk:
import Decorator.coffeebar.Drink
class NewMilk(obj: Drink) extends Decorator(obj) {
setDescription("新式Milk")
setPrice(4.0f)
}
Soy:
import Decorator.coffeebar.Drink
class Soy(obj: Drink) extends Decorator(obj) {
setDescription("Soy")
setPrice(1.5f)
}
ocp开闭原则:功能扩展开放,代码(已有的)修改关闭
----观看韩顺平老师Scala视频笔记