装饰者模式:动态的将责任附加到对象上,若要扩展功能,装饰者提供了比集成更有弹性的替代方案。
设计原则:
1:封装变化
2:多用组合,少用继承
3:针对接口编程,不针对实现编程
4:为对象之间的松耦合设计而努力
5:对修改关闭,对扩展开放
用Head First 设计模式中的例子。
一个咖啡店,会有各种类型的咖啡,还会给咖啡加上各种辅料,计算价钱的时候当然要加上辅料的价钱。
显然,可能不可能为每一种可能性的咖啡都写一个方法。类似于这种问题,就可以用到装饰者模式类解决。
直接看代码
abstract class Beverage { String description = "Unkwnown Beverage"; public String getDescripion() { return description; } public abstract double cost(); } abstract class CondimentDecorator extends Beverage { public abstract String getDescripion(); } class Espresso extends Beverage { public Espresso() { description = "Espresso"; } @Override public double cost() { return 1.00; } } class HouseBlend extends Beverage { public HouseBlend() { description = "House Blend Coffee"; } @Override public double cost() { return 0.70; } } class Mocha extends CondimentDecorator { Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; } @Override public String getDescripion() { return this.beverage.getDescripion() + ", Mocha"; } @Override public double cost() { return 0.20 + beverage.cost(); } } public class Test { public static void main(String[] args) { Beverage beverage = new Espresso(); beverage = new Mocha(beverage); beverage = new Mocha(beverage); System.out.println(beverage.getDescripion() + "$" + beverage.cost()); } }
类图:
这个例子上要表达的是什么意思?开始,咖啡只有一个cost方法,我们通过用装饰者模式给每种咖啡扩展了一个getDescription方法,同时又扩展了cost方法,现在这个cost方法是选择一个基础咖啡在继续加辅料把所有的钱加载一起。
设计原则:对修改关闭,对扩展开放(开闭原则)。
我们写代码的时候遵循设计原则固然是好的,但是不要为了遵循设计原则而去用设计原则,我们用这些原则的目的就是为了帮我们设计出更好代码,如果因为遵循了设计模式而让代码变的复杂,而且这个地方以后基本上也不会有什么扩展,那么就可以考虑不用这个设计模式。换句话说就是我们无法保证每个地方都应用设计模式,我们只需要在有可能改变的地方进行设计,而哪个是以后需要改变的地方,就看实际项目而定了。
还有一个之前说多的问题,组合优于继承,那么这个地方怎么还用到了继承。要明白,“组合优于继承”中的继承说的是利用集成获得了父类的行为(依赖继承)。而装饰者模式中用到的继承是未了让装饰者与被装饰者具有相同的类型(类型匹配),以便能让装饰者取代被装饰者。这里的行为来自装饰者(CondimentDecorator及其子类)和组件(各种咖啡)。如果是依赖继承,类的行为只能在编译时就决定。而装饰者模式可以在运行时,实现新的装饰者来增加新的行为。这里Beverage可以是一个抽象类,当然也可以设计成一个接口。
装饰者模式有能力为设计注入弹性,但是也有问题,上面的代码可以看到,会加入大量的类,而导致可能一般时候会看不懂。