设计模式-装饰模式
装饰模式 参考
装饰模式又名包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰模式的结构
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
装饰模式的角色有:
- 抽象构件(Component)角色: 给出一个抽象接口,以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色: 定义一个将要接收附加责任的类。
- 装饰(Decorator)角色: 持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- 具体装饰(ConcreteDecorator)角色: 负责给构件对象“贴上”附加的责任。
具体例子
使用装饰模式的方案来处理咖啡店订单
类图
package decorator.Demo /** * 抽象类,Component角色 * */ abstract class Drink { open var description: String = "" //getter缺省为默认 set(value) { //field关键字指向属性本身 field = value } open var price = 0.0f abstract fun cost(): Float }
package decorator.Demo /** * 具体构件角色, ConcreteComponent * */ open class Coffee : Drink() { override fun cost(): Float { return super.price } }
package decorator.Demo /** * 美式咖啡 * */ class AmericanCoffee : Coffee() { init { description = "American Coffee" price = 5.0f } }
package decorator.Demo /** * Espresso咖啡 * */ class Espresso : Coffee() { init { description = "Espresso" price = 6.0f } }
package decorator.Demo /** * 装饰者角色,Decorator, 相当于可以加到单品咖啡的调味品 * */ open class Decorator constructor(drink: Drink) : Drink() { //被装饰者对象,这里是单品咖啡 private var obj: Drink? = null init { this.obj = drink } override fun cost(): Float { //自已的价格 + 单品咖啡的价格 return super.price + obj!!.cost() } override var description: String = "" get() = "${super.description} ${super.price}元 && ${obj!!.description}" set(value) { //field关键字指向属性本身 field = value } }
package decorator.Demo /** * 具体装饰角色, ConcreteDecorator,这里是调味品牛奶 * */ class Milk constructor(drink: Drink) : Decorator(drink) { init { //此调味品的描述 description = "milk" //此调味品的价格 price = 2.0f } }
package decorator.Demo /** * 具体装饰角色, ConcreteDecorator,这里是调味品巧克力 * */ class Chocolate constructor(drink: Drink): Decorator(drink) { init { //此调味品的描述 description = "chocolate" //此调味品的价格 price = 3.0f } }
代码测试
class CoffeeShop { init { //订单要求: 一杯美式咖啡,加上:2份chocolate + 1份milk var order: Drink = AmericanCoffee() println("${order.description}, 费用: ${order.cost()}") //order 加1份milk order = Milk(order) println("加入一份: milk, 费用: ${order.cost()}, 描述: ${order.description}") //order 加1份chocolate order = Chocolate(order) println("加入一份: chocolate, 费用: ${order.cost()}, 描述: ${order.description}") } }
结果
American Coffee, 费用: 5.0 加入一份: milk, 费用: 7.0, 描述: 2.0元 && American Coffee 加入一份: chocolate, 费用: 10.0, 描述: 3.0元 && 2.0元 && American Coffee
装饰模式的优点
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
装饰模式的缺点
由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系多更的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都是很像。
设计模式在JAVA I/O库的中应用
装饰模式在Java语言中的最著名的应用莫过于Java I/O标准库的设计了。
由于Java I/O库需要很多性能的各种组合,如果这些性能都是用继承的方法实现的,那么每一种组合都需要一个类,这样就会造成大量性能重复的类出现。而如果采用装饰模式,那么类的数目就会大大减少,性能的重复也可以减至最少。因此装饰模式是Java I/O库的基本模式。