设计模式--装饰者模式

一、问题的产生
开一家咖啡店,售卖各式咖啡,
1、咖啡店1.0版


设计模式--装饰者模式_第1张图片
image.png

如果仅仅是售卖这4种咖啡,这个订单系统也能满足要求,但是,如果想加入各种调料,如:奶(Milk),摩卡(Mocha),或者覆盖上奶泡,订单系统就需要考虑到调料的部分,根据所加入的调料来计算不同的费用。
简单粗暴的修改订单系统:


设计模式--装饰者模式_第2张图片
image.png

这种方法很明显不行,如果牛奶价格上涨了怎么办?新增了一种调料怎么办?

2、咖啡店2.0版
利用实例变量和继承来设计调料部分


设计模式--装饰者模式_第3张图片
image.png
public class Beverage {
  //为milkCost,mochaCost等声明实例变量
  public double cost() {
    float condimentCost = 0.0;
    if (hasMilk()) {
      condimentCost += milkCost;
    }
    if (hasMocha()) {
      condimentCost += mochaCost;
    }
    …
    return condimentCost;
  }
}

public class DarkRoast extends Beverage {
  public double cost() {
    return 1.99 + super.cost();
  }
}

当调料价格改变、出现了新的调料,都需要改变超类Beverage中的方法,如果以后售卖新的饮料,如:茶(Tea),某些调料及不适合它了,但是Tea仍然会继承那些不适合的方法,这些都明显违反了开闭原则。

3、咖啡店3.0版
采用新想法:以饮料为主体,在运行时用调料来“装饰”饮料。
例如,摩卡和奶泡深焙咖啡,需要做的是:
A、拿一个深焙咖啡(DarkRoast)对象
B、以摩卡(Mocha)对象装饰它
C、以奶泡(Whip)对象装饰它
D、调用cost()方法,并依赖委托(delegate)将调料的价钱加上去


设计模式--装饰者模式_第4张图片
image.png

二、装饰者模式
装饰者模式:动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
装饰者和被装饰对象有相同的超类
可以用一个或多个装饰者包装一个对象
装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定目的
对象可以在任何时候被装饰,可以在运行动态的、不限量的使用装饰者来装饰对象
1、类图


设计模式--装饰者模式_第5张图片
image.png

2、运用到咖啡店上


设计模式--装饰者模式_第6张图片
image.png

3、新的设计
双倍摩卡豆浆奶泡拿铁咖啡怎么设计呢?
设计模式--装饰者模式_第7张图片
image.png

4、咖啡店3.0版的实现
A、设计抽象组件
设计模式--装饰者模式_第8张图片
image.png
//饮料Beverage是个抽象类,有两个方法,方法getDescription自己实现,方法cost 需要子类实现
public abstract class Beverage {
  String description="Unknown Beverage";
  public String getDescription(){
    return description;
  }
  public abstract double cost();
}

B、实现具体组件


设计模式--装饰者模式_第9张图片
image.png
//实现深焙咖啡DarkRoast
public class DarkRoast extends Beverage{
  public DarkRoast (){
    description = "DarkRoast ";
  }

  //返回的是Espresso的价格,无需考虑调料的价格
  @Override
  public double cost() {
    return 1.99;
  }
}

//实现浓缩咖啡Espresso
public class Espresso extends Beverage{
  …
}
…//实现其他口味的咖啡

C、设计抽象装饰者


image.png
//由类图可看出,抽象装饰者也是扩展自Beverage类
public abstract class CondimentDecorator extends Beverage() {
    public abstract String getDescription();
}

D、实现具体装饰者


设计模式--装饰者模式_第10张图片
image.png
//摩卡Mocha是一个装饰者,扩展自CodimentDecorator(CodimentDecorator扩展自Beverage)
public class Mocha extends CodimentDecorator{
  //使Mocha能够引用到Beverage,需要定义一个Beverage的实例变量
  private Beverage beverage;
  //还需要把这Beverage实例传递到Mocha的构造器中
  public Mocha(Beverage beverage){
    this.beverage = beverage;
  }
  //描述的饮料需要带上调料的描述
  @Override
  public String getDescription() {
    return beverage.getDescription()+" Mocha";
  }
  //先把具体口味饮料Beverage的价格获取到,然后加上调料Mocha的价格,为最终的结果
  @Override
  public double cost() {
    return beverage.cost()+0.55;
  }
}

//实现牛奶调料
public class Milk extends CodimentDecorator{
  …
}
…//其他调料

E、测试代码Main

public class Main {
  public static void main(String[] args) {
    //订一杯深焙咖啡DarkRoast,不加调料,打印出描述和价格
    Beverage b1 = new DarkRoast();
    System.out.println(b1.getDescription()+" cost:"+b1.cost());

    //制造出一个浓缩咖啡Espresso对象
    Beverage b2 = new Espresso();
    //用摩卡Mocha装饰它
    b2 = new Mocha(b2);
    //用牛奶Milk装饰它
    b2 = new Milk(b2);
    System.out.println(b2.getDescription()+" cost:"+b2.cost());

    Beverage b3 = new …//其他口味及添加各种调料的饮料
  } 
}

Tips:小优化
对于各种口味的饮料DarkRoast,Espresso等,以及各种调料Mocha,Milk等,可以使用工厂模式来实现

三、在Java中的应用
读取文件数据


设计模式--装饰者模式_第11张图片
image.png

你可能感兴趣的:(设计模式--装饰者模式)