装饰者模式:
动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
新的包装类肯定要持有原有基类的句柄,然后覆盖超类中的方法。我们把被包装的类传入包装类,新的包装类就可以调用被包装类的方法并在此基础上做出改动。因为面向对象语言的上溯造型,在需要被包装类的地方完全可以提供包装类。
Java语言的I/O流系统就是装饰者模式的非常典型的代表。 示例:星巴兹咖啡订单系统。星巴兹提供多种口味咖啡并且提供向咖啡中添加各种调料的服务。
错误实现1:
定义一个Beverage抽象超类,店内所有饮料必须继承此类。其含有一个cost()抽象方法,子类必须定义自己的实现。
但是这样一来,添加不同的调料需要写不同的cost(),更进一步,如果调料可以添加多种或者指定双份呢?这种方法不禁实现困难,而且维护困难。如果牛奶价格上涨,每一个包含牛奶的方法都要改动。
错误实现2:
将各种调料以实例变量的形式加入超类中,利用实例变量和继承,就可以追踪这些调料。
这样设计,调料的价格改变会使我们更改现有代码;一旦出现新的调料,就要添加新的方法;以后可能会添加新的饮料,对这些新饮料来说一些调料可能并不适合添加。
设计原则--类应该对扩展开放,对修改关闭。
正确实现:
例如需要一份摩卡奶泡深焙咖啡:1、拿出一个深焙咖啡对象;2、用摩卡对象装饰它;3、用奶泡对象装饰它;4、调用cost()方法,并依赖委托将调料价格加上去。
具体Java实现中需要注意:
代码实现:
//饮料超类
public abstract class Beverage{
String description = "null"; //饮料的名称
public String getDescription(){//返回饮料的名称
return description;
}
//计算饮料价格的抽象方法
public abstract double cost();
}
//调料抽象类,也就是装饰者类,装饰者类是饮料超类的一个子类,所以以后的各种包装者类都是饮料超类的子类。
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();//所有调料装饰者都必须重新弄实现该方法
}
//实现Espresso饮料
public class Espresso extends Beverage{
public Espresso(){ description = "Espresso"; }
//重写价格方法
public double cost(){ return 1.99; }
}
//实现添加摩卡的Espresso饮料
public class Mocha extends CondimentDecorator{
Beverage beverage; //持有饮料超类类的句柄
public Mocha(Beverage beverage){
this.beverage = beverage;
}
//重写饮料名称方法
public String getDescription(){
return beverage.getDescription() + ",Mocha";
}
//重写计算价格方法
public double cost(){ return .20 + beverage.cost(); }
}
要点: