设计模式有一个重要的原则:类对扩展开放,对修改关闭。装饰者模式对喜欢继承的人来说是一个新的设计眼界,但却是再熟悉不过了。有这样一个例子:
冲泡咖啡:
1、拿一个深焙咖啡来冲泡。
2、感觉咖啡太苦了,放点摩卡修饰它,味道好多了。
3、可是我还想加些奶泡装饰它,这样就更好看了。
4、最后在付款的时候价格也会增加。
上面的例子展示的是我们对咖啡进行扩展的过程。目的是为了说明我们要允许类容易扩展,在不修改现有代码的情况下,就可以搭配新的行为。这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。但是如何设计才能实现可以扩展,有又禁止修改呢?
定义装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
下面我们看一个代码例子。
对咖啡做扩展后就该称为饮料(Beverage)了
//定义成抽象类
public abstract class Beverage{
String description="Cafe Beverage";
//在此做了实现
public String getDescription(){
return description;
}
//在子类中做具体实现
public abstract double cost();
}
Beverage很简单。我们再来实现调料(Condiment)抽象类,也就是装饰者类:
public abstract class CondimentDecorator extents Beverage{
//所有的调料装饰者都必须重新实现getDescription()方法。为啥呢?稍后会知晓。
public abstract String getDescription();
}
已经有了饮料基础类,让我们先实现一个加糖饮料(SugarCaffee)。
public class AddSugarCaffee extends Beverage{
public AddSugarCaffee(){
decription="SugarCaffee";
}
public double cost(){
return 1.99;
}
}
再定义另一种饮料--奶泡(AddMilkCaffee)
.
public class AddMilkCaffee extends Beverage{
//这种饮料喝SugarCafe做法一样,只是修改了配方,相应的描述也做一下修改。
public AddMochaCaffee(){
decription="MochaCaffee";
}
public double cost(){
return 0.98;
}
}
如果你回头去看看装饰者模式的类图,将发现我们已经完成了抽象组件(Beverage),有了具体组件(AddSagarCaffee和 AddMilkCaffee ),也有了抽象装饰者( CondimentDecorator )。现在,我们就来实现具体装饰者.
Mocha是一个装饰者,所以扩展自CondimentDecorator,别忘了CondimentDecorator也扩展自Beverage。
public class Mocha extends CondimentDecorator{
//用一个实例变量记录饮料,也就是被装饰者
Beverage beverage;
//把饮料当做构造器的参数,再由构造器将此饮料记录在实例变量中。
public Mocha(Beverage beverage){
this.beverage=beverage;
}
public String getDescription(){
//我们希望描述不只是描述饮料,而是完整地连调料都描述出来。所以先用委托的做法,得到一个叙述。
//然后在其后加上附加的调料。把之前加过的调料都可以打印出来。
return beverage.getDescription()+",Mocha";
}
public double cost(){
//0.20为Mocha调料的价钱。
return 0.20+beverage.cost;
}
}
同样在定义一个大豆Soy装饰者。也扩展自 CondimentDecorator。
public class Soy extends CondimentDecorator{
Beverage beverage;
public Soy(Beverage beverage){
this.beverage=beverage;
}
public String getDescription(){
return beverage.getDescription()+",Soy";
}
public double cost(){
return 0.20+beverage.cost;
}
}
现在各种调料都有了,我们实例化一个对象,然后用调料来装饰它。
public class StarbuzzCaffee{
public static void main(String args[]){
Beverage beverage=new AddSugarCaffee();
//打印出只加入一次糖的咖啡饮料。
System.out.println(beverage.getDescription()+"$"+beverage.getCost());
Beverage beverage2=new AddMilkCaffee();
beverage2==new Mocha(beverage2);//第一次加入摩卡,用摩卡装饰
beverage2=new Mocha(beverage2);//第二次加入摩卡
beverage2=new Soy(beverage2);//在两次加入摩卡的基础上,再加入大豆装饰。
//打印出来的结果将是“Mocha Mocha Soy $ 0.8”
System.out.println(beverage2.getDescription+"$"+beverage2.getCost);
}
}
继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。装饰者模式可以在被装饰者的行为前面与后面加上自己的行为,甚至将被装饰者的行为整个取代,而达到特定的目的。你可以用无数个装饰者包装一个组件,但是,这也会导致设计中出现许多小对象,如果过度使用,反而让程序变得很复杂。
总之:装饰者模式提倡,类,应该对扩展开放,对修改关闭。
上图:最直观!