开发者学堂课程【Scala 核心编程 - 进阶:装饰者模式介绍和咖啡店应用实例】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/610/detail/9139
装饰者模式介绍和咖啡店应用实例
内容介绍
一、原理
二、设计方案
三、代码
四、举例
五、总结
一、原理
装饰者模式就像打包快递一样,例如在淘宝上买了陶瓷产品或者买了一件衣服,商家先用报纸或者塑料泡沫包裹,主体就是陶瓷或者衣服,主体叫 component,由主体引申出 concretecomponent,concrete 是混凝土的意思,decorator 就是装饰者,可以理解成各种调料,这样就形成新的关系,
在 component 和 creatcomponent 之间,可以设计缓冲层,也可以不设计, decorator 也会继承 drink,两者都成为了 component 的子类,就可以进行装饰。
比如 concrete 对应具体的主体,比如单品咖啡,而decorator认为是装饰者,因为喝咖啡可以不要调料,调料是可选的,装饰者是附着在上面的。
所谓装饰者就是动态的将新功能附加在对象上,在对象功能扩展方面,比继承更加的有弹性,装饰者也充分的体现出开闭原则。
二、设计方案
通过装饰者模式就有变化了,首先drink是抽象的,而单品咖啡可以独立出来,而decreator装饰者里面有各个调料。
说明:
Drink这个超类和前面基本一样;
Shortblack等单品咖啡的设计也和之前一样;
Decorator是装饰类,包含了被装饰的对象;
Decorator的cost费用要进行叠加,因为装饰者可以装饰,还可以继续装饰。
(1)有一份Milk+LongBlack【相当于是Milk装饰了
LongBlack】 Milk是装饰者,LongBlack是主体;
(2)使用 chocolate装饰了一份 Milk+LongBlack;
(3)使用 chocolate装饰 chocolate+LongBlack+Milk,层叠的进行装饰,当计算成本时要递归的计算,
三、代码
首先对照图
Drink 表示饮品,是抽象类,最主要抽象描述,因为饮品是个抽象的,所以是抽象方法,将计算成本的方法做成抽象方法,然后继承。
出现以下报错:
Package com.atguigu.chapter17.decorator.coffeebar.mycoffee
class Espresso extends coffee {
//使用主构造器
super.setDescription(“Espresso”)
super.setPrice(6.0f)
}
coffee 地方报错,因为在 component 和 concrete 之间做了一层缓冲层,因为从扩展性来说,将来可能咖啡店扩展了,除了有咖啡这种产品还有传统饮品,如果仅仅针对这个案例来说,可以不要,此处是为了扩展,意义并不大。
单品咖啡在装饰者设计模式中就是concrete:
packagecom. atguigu.chapter17.decorator .coffeebar.mycoffee
class Espresso extends Coffee {
super . setDescription("Espresso")
super . setPrice(6.0f)
}
decorator 是关键,是装饰者模式体现的价值:
class Decorator extends Drink {
private var obj: Drink =null
def this(obj: Drink) {
this.obj=obj
}
override def cost(): Float={
super . getprice()+obj.cost()
}
首先仍然继承 drink,object 是被装饰的, object可以是单品咖啡,也可以咖啡加调料的组合,计算成本实现了 COS 的方法,首先先得到自己的咖啡,然后递归的调用,因为是不停的装饰。
获取信息时,也要递归。chocolate 要继承 decorator,在巧克力里面放 drink,然后扩展它。放到体系里面,decorator 也是 drink 的子类,以此类推,牛奶也不停的往上继承。
使用时,直接调订单里面cost得到描述,因为无音咖啡继承coffee,而coffee又继承了 drink,订单一的价格就是三块钱,运行结果如下:
orde1 price: 3.0
order1 desc :Decaf 价格:3.0
如果需求是点一份longblack,并且加入两份巧克力代码如下:
Var order2:Drink=new Longblack
因为用装饰者模式,在定milk的时候,直接放进去即可,因为milk继承装饰者,所以就可以用decorator相应的方法,也把对象传给了decorator,decorator把对象交给了装饰者,一步一步传到decorator,
此时运行结果:
order2 price:7.0
order2 desc:Milk 价格:2.0&&LongBlack价格:5.0
假如还要再加两份巧克力和一份milk,结果如下:
order2 price:10.0
order2 desc:chocolate 价格:3.0&&Milk 价格:2.0&&LongBlack价格:5.0
如果还要再来一份咖啡,再包裹即可。当在目前基础上去增加新的饮品时,就不需要动整个类的体系结构了。
比如有一种新式的牛奶,复制一份即可,把名字或价格更改即可,价格也可以算出。如果单品咖啡再加一份,也是相同道理。如果需求特别复杂时就可以使用该设计模式。
四、举例
Java 中的 io 流里面有叫 fiilterinputstream,就是装饰者,inputstream就是被装饰者,
示意图如下:
如果想加入过滤的功能,继承之后然后把被装饰包裹,文件流也具有了过滤的功能,fiilterinputstream就是装饰者,而inputstream就是被装饰者。
五、总结
先提出需求,引起思考,需求是咖啡店有单品咖啡、调料,要求在扩展咖啡种类,用oo来计算不同种类咖啡的费用,可以单点可以组合。最原始的方法,会形成会出现类爆炸的情况。
第二种方案最大的问题就是将来会直接去修改代码,违反了ocp的原则,将来缺口会变得越来越大,应该尽量避免。
第三种方案是就是装饰者模式
在主体就是component,包装就是decorator,于是提出设计方案,先抽象出来一层,因为可能有很多子类,主体就是陶瓷或者衣服,主体叫component,由主体引申出concretecomponent,concrete是混凝土的意思,decorator就是装饰者,可以理解成各种调料。主体是最重要,装饰者是单品咖啡,装饰者就是动态的将新的功能附加在对象上,动态很重要,不需要改源代码,比继承更加的有弹性,充分利用递归的特性,也体现了开闭原则。先用装饰者设计模式设计方案如下: