装饰者模式
编写程序时,利用继承设计子类的行为是在编译时静态决定的,且所有子类都会继承到相同的行为;相反,利用组合的做法扩展对象的行为,可以在运行时动态地扩展,在使用新代码添加新功能的同时,无须修改现有代码,进而减少产生bug的机会
假设现在有一个超类Beverage,其中的属性代表了各式调料,cost()方法会计算所有调料的价钱;为了提供各式各样的饮料,每个子类都需要继承超类Beverage,并重写cost()方法,扩展超类的功能,计算该饮料的价钱
开闭原则
但这种设计存在一个问题:出现新调料的时候需要修改超类Beverage,以及某些饮料可能继承了一些不合适的方法
显然,这种设计违反了开闭原则
开闭原则:类应该对扩展开放,对修改关闭
改进:以饮料为主体,在运行时以调料来“装饰”饮料,如:当顾客想要一杯加奶泡(Whip)的摩卡(Mocha时),先创建一个DarkRoast对象,然后创建一个Mocha对象包裹DarkRoast,然后再创建一个Whip对象包裹Mocha,当客户需要付款时,调用最外圈的装饰者(此处为Whip)的cost(),而cost()调用所装饰对象(此处为Mocha)的cost()递归计算价钱即可
定义
装饰者模式动态地将责任附加到对象上;若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
特点
- 装饰者和被装饰对象具有相同的超类型
- 可用一个或多个装饰者包装一个对象
- 任何需要原始对象(被包装的,如:DarkRoast)的场合,可以使用包装过的对象(如:Whip)代替
- 装饰者可以在所委托被装饰者的行为之前/之后,加上自己的行为,以达到特定的目的(类似于AOP,达到扩展目的)
- 对象可以再任何时候被装饰,所以可以在运行时动态地、不限量地使用任何一个装饰者来装饰
实现
- ConcreteComponent是将要动态地加上新行为的对象,即被装饰者
- Decorator与ConcreteComponent实现共同的接口或抽象类,同时它也是装
- ConcreteDecoratorA和ConcreteDecoratorB有一个实例变量保存某个component的引用(即包装一个组件),同时也可以扩展compoent的状态,也可以添加新方法newBehavior()在被装饰者的旧行为前后调用,做一些额外的计算
所以,对于原来的Beverage设计,我们可以使用装饰者模式改进为
优缺点
优点
- 装饰者与组件组合时,就加入了新的行为。该新行为,并不是继承自超类,而是由组合对象得来的,即:可以在任何时候实现新的装饰者增加新的行为。如果依赖继承,每当需要新行为时,还需要修改现有代码。
缺点
- 有时候会加入大量小类,不容易理解
- 当代码依赖某种特殊类型时,使用装饰者容器产生错误
- 使用装饰者模式,不仅需要实例化组件,还需要包装组件,增加代码复杂度