装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者模式提供了比继承更有弹性的替代方案。
继承的缺点:类的数量爆炸、设计死板,以及基类加入的新功能不适用所有子类。
利用继承来设计子类的行为,是在编译时静态地决定的,而且所有子类都会继承到相同的行为。用组合扩展对象的行为,就能在运行时动态地扩展行为。而且使用组合,可以动态地组合对象,写新的代码添加新的功能,而不需要修改原来的代码,减少引进bug的风险。
设计原则:类应该对扩展开放,对修改关闭。
允许类容易扩展,在不修改原来代码的情况下,就可以搭配新的行为。
遵循开放-关闭原则,会引入新的抽象层次,增加代码的复杂度,所以需要把精力集中在设计中最有可能改变的地方来使用这一原则。
- 装饰者和被装饰者有相同的超类型 (利用继承达到类型匹配,而不是为了获取基类行为);
- 可以用一个或者多个装饰者包装一个对象;
- 由于有相同的超类,所以任何需要使用被装饰的对象时,都可以用装饰者的对象代替它;
- 装饰者可以在所委托的被装饰者的行为之前或者之后,加上自己的行为;
- 被装饰的对象,可以在运行时动态不限量的使用任何装饰者来装饰对象。
装饰者模式实例类图:
- 装饰者CondimentDecorator和被装饰者Expresso、HouseBlend、DarkRoast等具有相同的超类型Beverage。
- 可以用一个或者多个CondimentDecorator装饰者(如Milk、Mocha、Soy等)来包装一个被装饰对象(如Expresso、HouseBlend、DarkRoast等的对象)。
例如:
Beverage beverage = new HouseBlend();
beverage = new Mocha(beverage); //用Mocha对象来包装HouseBlend对象 - 装饰者Mocha对象可以在被装饰者HouseBlend对象的cost调用之后加上自己的cost定义的行为(参见代码)
装饰者对象持有超类型Beverage的引用,这样就可以使用超类型的引用来添加自己的行为。 - 被装饰者HouseBlend对象可以在运行时动态不限量使用任何装饰者Milk、Mocha、Soy等对象来装饰自己。
Beverage beverage2 = new HouseBlend(); //HouseBlend对象是被装饰者对象
beverage2 = new Mocha(beverage2); //被Mocha装饰
beverage2 = new Soy(beverage2); //被Soy装饰
beverage2 = new Whip(beverage2); //被Whip装饰
对象被装饰者对象装饰,体现在代码中就是:被装饰者对象 = 装饰者对象;
代码实现:
/**
*
* @author Yves
*/
public abstract class Beverage {
public String description = "Unknown Beverage";
public String getDescription(){
return this.description;
}
public abstract double cost();
}
超类型Beverage
public class DarkRoast extends Beverage {
public DarkRoast() {
super.description = "DarkRoast";
}
@Override
public double cost() {
return 0.99;
}
}
被装饰者DarkRoast
public class HouseBlend extends Beverage{
public HouseBlend(){
super.description = "House blend coffee";
}
@Override
public double cost() {
return 0.89;
}
}
被装饰者HouseBlend
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
装饰者抽象类CondimentDecorator与被装饰者DarkRoast和被装饰者HouseBlend具有相同的超类型Beverage
public class Mocha extends CondimentDecorator{
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return beverage.cost() + 0.20;
}
}
装饰者子类Mocha持有超类型Beverage引用,并利用该引用在cost方法中添加自己的行为beverage.cost() + 0.20;
public class Soy extends CondimentDecorator{
Beverage beverage;
public Soy(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
@Override
public double cost() {
return beverage.cost() + 0.15;
}
}
装饰者子类Soy持有超类型Beverage引用,并利用该引用在cost方法中添加自己的行为beverage.cost() + 0.15;
public class Whip extends CondimentDecorator{
Beverage beverage;
public Whip(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
@Override
public double cost() {
return beverage.cost() + 0.10;
}
}
装饰者子类Whip持有超类型Beverage引用,并利用该引用在cost方法中添加自己的行为beverage.cost() + 0.10;
public class StarbuzzCoffee {
public static void main(String args[]){
Beverage beverage = new HouseBlend();
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage2 = new DarkRoast(); //DarkRoast is the concrete class which can be decorated.
beverage2 = new Mocha(beverage2); //Decorate by Mocha
beverage2 = new Whip(beverage2); //Decorate by Whip
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
}
}
被装饰者DarkRoast,beverage2.cost() –> 0.99
内圈装饰者Mocha,beverage2.cost() –> 0.20
外圈装饰Whip,beverage2.cost() –> 0.10
最后beverage2.cost()返回结果1.29
run:
House blend coffee $0.89
DarkRoast, Mocha, Whip $1.29
调用关系图:
首先调用外层装饰者的cost方法,然后依次调用内层装饰者的cost方法,直到调用被装饰者的cost方法;首先被装饰者cost方法返回,然后最内层装饰者cost方法返回,然后依次返回外层装饰者cost方法,直到最外层装饰者cost方法返回。