一、含义
动态地给一个对象添加一些额外的职责。就增加功能来说, Decorator模式相比生成子类更为灵活。装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。
分析含义:以前如果要扩展功能,一般都是使用继承基类,然后根据不同的特征进行拓展,这样做会使得代码不够灵活,因为继承是在运行之前就写好了的,当我们想要改变或者增加一些东西时,会直接改变类,这就破坏了开放与关闭原则,装饰者原则使用组合的思想,相对比继承只能纵向扩展,装饰者使得可以横向组合,装饰者和本身的组件从源头上都是继承同一个接口,这样就可以利用多态,使得类型达到相同,从而使得相互组合变得容易。
二、举例
应用场景:为咖啡店设计订单系统,使得满足不同饮料的需求。
- 策略一:使用一个饮料抽象类,里面是一些公有的必须要子类实现的行为(比如:描述饮料函数、计算价格函数),其子类就是所有拥有的饮料。
- 策略二:在饮料抽象类中,加入一些变量用于判断是否加入了这些佐料(bool值),同时还应加入设置是否加入某种调料的函数(set)、获取是否添加了某种调料的函数(get),计算总价的方法(这里不应该是抽象方法了,应该根据get函数的bool值得到不同的调料总价,然后对于一款具体的饮品类应该在覆盖基类cost函数时,返回基本的价格同时加上所有调料的总价)。
- 策略三:与策略二有相似的地方,但是角度不同。策略二是让所有调料在基类中,每一个具体饮品返回自身基础价格和基类中调料总值。策略三是通过接口使得调料和主料达到类型相同(源头接口),使得调料可以包含住主料的同时,最后能够返回一个饮料类型,对于价格的计算,应该是从最外层的装饰者开始,每一层的计算价格的函数都调用内层的cost函数加上本层装饰的价格直到最后在基础组件时返回一个基础价格(与递归函数相似)。
策略评价:
- 对于策略一:每新增一种饮料就需要对应写一种具体类,这就会造成类爆炸。如下图所示:
- 对于策略二而言:虽然可以判断是否加了佐料,但是当加入的佐料是两份相同的佐料,用这种模式怎么实现呢?如果我要新增一个佐料,还需要在基本抽象类中新增对新的佐料的设置和获取。并且由于每一种饮品并不是都要有某种佐料,如果单纯直接继承,那么会继承一些可能不合适的特点,这种设计不是很合理。
- 对于策略三,相对要好一点,我们要保证主要组件和装饰的源头保持一致,我们通过抽象类来实现主材料和装饰材料的用户匹配。主材料和装饰材料的不同主要是装饰材料内部有一个基本类,用来装主材料。
*******************************************************************************************
代码实现:
****************************************************************************************************************
测试类:
不论装饰者里面包裹的是装饰者还是基本材料,始终最里面包裹的是一个基本材料类,基本材料类相当于递归的出口,最后返回一个数值。
****************************************************************************
拓展:对于策略二中,cost函数应该如何写?
书中这个代码不完善我目前的思路是可以考虑设置一个枚举,装TALL、GRANDE、VENTI,然后在基本的set函数中需要对size字段传入相应的0、1、2进行传值,再进行对比。