装饰者模式
动态的将责任附加到对象身上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案
问题
看一个饮料喝调料的问题
1,消费者需要一杯咖啡
2,消费者提出要求:要加糖
3,消费者提出要求:要加两份牛奶
这个时候你会用什么方式来解决呢?
解决思路
1,使用最原始的方法,记住每一种调料的价格和饮料的价格,最后+在一起。
如果消费者忽然不要某种饮料呢?
减去相应的价格
或者说想要发票?
。。。。
如果饮料种类非常多呢?
。。。。
怎么办呢?
2,使用装饰者,把饮料看做为被装饰者,调料为装饰者。
如果消费者忽然不要某种饮料呢?
删除对应的装饰者
或者说想要发票?
每种调料内都添加描述即可
种类非常多
创建 package 进行分类,如 碳酸饮料:,分为哪几种(可乐,雪碧等在一个package),调料分为哪几种(加冰,姜汁等,在一个 package中)!
实现
1,首先定义饮料的抽象类
/** * @name ModeDemo * @class name:com.example.mode.decorate * @author 345 QQ:1831712732 * @time 2020/1/6 21:29 * @description 饮料 */ abstract class Beverage { /** * 未知的饮料 */ var mDescription = "Unknown Beverage" /** * 获取饮料描述 */ open fun getDescription(): String { return mDescription } /** * 计算价格 */ abstract fun cost(): Double }
2,创建饮料
/** * @name ModeDemo * @class name:com.example.mode.decorate * @author 345 QQ:1831712732 * @time 2020/1/6 21:36 * @description 被装饰者:浓缩咖啡 */ class Espresso : Beverage() { init { mDescription = "浓缩咖啡" } /** * 浓缩咖啡的价格 */ override fun cost(): Double { return 32.00 } }
/** * @author 345 QQ:1831712732 * @name ModeDemo * @class name:com.example.mode.decorate * @time 2020/1/6 21:38 * @description 被装饰者:混合咖啡 */ class HouseBlend : Beverage() { init { mDescription = "混合咖啡" } override fun cost(): Double { return 50.00 } }
3,创建调料抽象类
/** * @name ModeDemo * @class name:com.example.mode.decorate * @author 345 QQ:1831712732 * @time 2020/1/6 21:33 * @description 装饰者类(调料),例如给咖啡添加牛奶,糖等,需要继承此类 */ abstract class CondimentDecorator : Beverage() { /** * 调料的描述 */ abstract override fun getDescription(): String }
4,创建具体的调料类
/** * @name ModeDemo * @class name:com.example.mode.decorate.seasoning * @author 345 QQ:1831712732 * @time 2020/1/6 21:46 * @description 装饰者:牛奶 */ class Milk(private val beverage: Beverage) : CondimentDecorator() { override fun getDescription(): String { return beverage.getDescription() + ",牛奶" } /** * 价格 */ override fun cost(): Double { return 5.00 + beverage.cost() } }
/** * @name ModeDemo * @class name:com.example.mode.decorate.seasoning * @author 345 QQ:1831712732 * @time 2020/1/6 21:55 * @description 装饰者:糖 */ class Sugar(private val beverage: Beverage) : CondimentDecorator() { /** * 描述 */ override fun getDescription(): String { return beverage.getDescription() + ",糖" } /** * 价格 */ override fun cost(): Double { return 4.00 + beverage.cost() } }
/** * @name ModeDemo * @class name:com.example.mode.decorate.seasoning * @author 345 QQ:1831712732 * @time 2020/1/6 21:58 * @description 装饰者:奶油 */ class Whip(private val beverage: Beverage) : CondimentDecorator() { override fun getDescription(): String { return beverage.getDescription() + ",奶油" } /** * jia价格 */ override fun cost(): Double { return 6.00 + beverage.cost() } }
5,测试
/** * @name DesignModeDemo * @class name:com.example.mode.decorate * @author 345 QQ:1831712732 * @time 2020/1/6 22:00 * @description */ fun main() { /** * 一杯浓缩咖啡,加糖,牛奶 */ val milk = Milk(Sugar(Espresso())) println(milk.getDescription() + " ¥" + milk.cost()) /** * 一杯杯混合咖啡,加牛奶,两份奶油 */ val whip = Whip(Whip(Milk(HouseBlend()))) println(whip.getDescription() + " ¥" + whip.cost()) }
结果
浓缩咖啡,糖,牛奶 ¥41.0
混合咖啡,牛奶,奶油,奶油 ¥67.0
解决问题
**1,**如果消费者忽然不要某种饮料呢?例如:两份牛奶换成一份
这个比较麻烦了,需要修改代码(一般情况不会这样做,下面会给出解释)。给调料抽象类添加如下代码:
abstract class CondimentDecorator : Beverage() { /** * 调料的描述 */ abstract override fun getDescription(): String abstract fun setCondiment(condiment: CondimentDecorator) }
实现类
class Whip(private var beverage: Beverage) : CondimentDecorator() { override fun setCondiment(condiment: CondimentDecorator) { beverage = condiment } override fun getDescription(): String { return beverage.getDescription() + ",奶油" } /** * 价格 */ override fun cost(): Double { return 6.00 + beverage.cost() } }
其他的实现类都是如此
测试
fun main() { /** * 一杯杯混合咖啡,加牛奶,两份奶油 */ val whip = Whip(Whip(Milk(HouseBlend()))) println(whip.getDescription() + " ¥" + whip.cost()) val houseBlend = HouseBlend() val listOf = mutableListOf<CondimentDecorator>() //两份奶油,一份牛奶 listOf += Whip(houseBlend) listOf += Whip(houseBlend) listOf += Milk(houseBlend) for (i in 0 until listOf.size) { if (i < listOf.size - 1) { listOf[i].setCondiment(listOf[i + 1]) } } println(listOf[0].getDescription() + " ¥" + listOf[0].cost()) //取消一个牛奶,这里可以直接删除引用 listOf.removeAt(0) println(listOf[0].getDescription() + " ¥" + listOf[0].cost()) }
混合咖啡,牛奶,奶油,奶油 ¥67.0
混合咖啡,牛奶,奶油,奶油 ¥67.0
混合咖啡,牛奶,奶油 ¥61.0
看起来麻烦了一些。其实代码还是很好理解的
原来都是通过构造方法进行装饰,但是现在增加了一个 set 方法。通过 set 也可以进行装饰
代码还可以进行更好的优化
当然了,一般情况下也不需要这种代码。因为装饰是一次性的。就像 JAVA 的 IO 一样,也是用的是装饰者模式,你不可能装饰后在减掉某个装饰板。这里只是一个扩展
2,3
这两个问题都差不多。只要添加相应的调料和饮料即可。
要点
装饰者模式意味着一群装饰者类,这些类用于包装具体的组件
你可以使用无数个装饰者包装一个组件
装饰者会导致程序中出现过多的小对象。如果过度使用,会让程序变得很复杂。
一旦装饰后,一般情况下,就不能取消某个装饰了。当然你可以使用我上面的做法,并做一下适当的修改。但是不推荐这种做法