美文网首页
设计模式--装饰者模式

设计模式--装饰者模式

作者: Qi0907 | 来源:发表于2018-07-30 14:30 被阅读0次

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


    image.png

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


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

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


    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)将调料的价钱加上去


    image.png

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


    image.png

    2、运用到咖啡店上


    image.png
    3、新的设计
    双倍摩卡豆浆奶泡拿铁咖啡怎么设计呢?
    image.png
    4、咖啡店3.0版的实现
    A、设计抽象组件
    image.png
    //饮料Beverage是个抽象类,有两个方法,方法getDescription自己实现,方法cost 需要子类实现
    public abstract class Beverage {
      String description="Unknown Beverage";
      public String getDescription(){
        return description;
      }
      public abstract double cost();
    }
    

    B、实现具体组件


    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、实现具体装饰者


    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中的应用
    读取文件数据


    image.png

    相关文章

      网友评论

          本文标题:设计模式--装饰者模式

          本文链接:https://www.haomeiwen.com/subject/zjlfvftx.html