设计模式学习(五) -- 装饰者模式 Decorator Pattern

来源:互联网 发布:中国统计局数据库 编辑:程序博客网 时间:2024/06/05 20:25

装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者模式提供了比继承更有弹性的替代方案。

继承的缺点:类的数量爆炸、设计死板,以及基类加入的新功能不适用所有子类。

利用继承来设计子类的行为,是在编译时静态地决定的,而且所有子类都会继承到相同的行为。用组合扩展对象的行为,就能在运行时动态地扩展行为。而且使用组合,可以动态地组合对象,写新的代码添加新的功能,而不需要修改原来的代码,减少引进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.89DarkRoast, Mocha, Whip $1.29

调用关系图:
装饰者模式实例调用关系图

首先调用外层装饰者的cost方法,然后依次调用内层装饰者的cost方法,直到调用被装饰者的cost方法;首先被装饰者cost方法返回,然后最内层装饰者cost方法返回,然后依次返回外层装饰者cost方法,直到最外层装饰者cost方法返回。

0 0