Java 设计模式-装饰者模式

180 阅读3分钟
  1. 需求分析:
    现在有一家咖啡店, 提供不同类型的咖啡, 但是顾客在购买咖啡的同时可以要求加入各种调料. 咖啡店会根据加入的调料收取不同的费用. 假设现在有 4 种调料.

    /**
     * 抽象的饮料类
     */
    public abstract class Beverage {
        private Stirng description;
        private boolean hasMilk;
        private boolean hasSoy;
        private boolean hasMocha;
        private boolean hasWhip;
        
        protected abstract double getCost();
        
        public void getDescription() {
            return description;
        }
        
        public boolean hasMilk() {
            return this.hasMilk;
        }
        
        public void setMilk(boolean hasMilk) {
            this.hasMilk = hasMilk;
        }
        
        public boolean hasSoy() {
            return this.hasSoy;
        }
        
        public void setSoy(boolean hasSoy) {
            this.hasSoy = hasSoy;
        }
        
        public boolean hasMocha() {
            return this.hasMocha;
        }
        
        public void setMocha(boolean hasMocha) {
            this.hasMocha = hasMocha;
        }
        
        public boolean hasWhip() {
            return this.hasWhip;
        }
        
        public void setWhip(boolean hasWhip) {
            this.hasWhip = hasWhip;
        }
    }
    
    /**
     * 具体类型的咖啡-深焙咖啡
     */
    public class DarkRoast extends Beverage {
        public DarkRoast() {
            this.description = "Dark Roast";
        }
        
        public double getCost() {
            // 基本价钱
            double cost = 1.0;
            if (hasMilk()) {
                cost += 0.1;
            }
            if (hasSoy()) {
                cost += 0.2;
            }
            if (hasMocha()) {
                cost += 0.3;
            }
            if (hasWhip()) {
                cost += 0.4;
            }
            return cost;
        }
    }
    
    /**
     * 客户端
     */
    public class Client {
        public static void main(String[] args) {
            DarkRoast darkRoast = new DarkRoast();
            darkRoast.setMilk(true);
            darkRoast.setMocha(true);
            System.out.println(darkRoast.getCost());
        }
    }
    

    现在的问题是咖啡的类型有很多, 加入现在某种调料的价钱发生了改变, 每种咖啡类的 cost() 方法就需要进行调整; 如果出现新的调料, 每种咖啡类的cost() 方法又需要增加判断; 对于某些调料来说, 某些咖啡类型并不能添加, 但是在类中仍然继承了这些调料以及相关的方法; 顾客还可能需要将某种调料增加好几倍, 例如需要加双倍摩卡的咖啡. 可以采用装饰者模式来解决这个问题.

    首先创建一个深焙咖啡对象, 然后以摩卡装饰它, 然后再用摩卡装饰它, 这样就可以得到加双倍摩卡的咖啡了. 如果需要其它的调料, 可以用其它的调料继续装饰它, 最终就可以得到想要的咖啡了.

    /**
     * 抽象的饮料类
     */
    public abstract class Beverage {
        protected String description;
        
        public String getDescription() {
            return this.description;
        }
        
        public abstract double cost();
    }
    
    /**
     * 抽象的装饰者类
     */
    public abstract class CondimentDecorator extends Beverage {
        public abstract String getDescription();
    }
    
    /**
     * 具体的咖啡类-浓缩咖啡, 这时候的咖啡没有增加任何调料
     */
    public class Espresso extends Beverage {
        public Espresso() {
            this.description = "Espresso";
        }
        
        public double getCost() {
            // 基本价钱
            return 1.0;
        }
    }
    
    /**
     * 摩卡调料
     */
    public class Mocha extends CondimentDecorator {
        private Beverage beverage;
        
        public Mocha(Beverage beverage) {
            this.beverage = beverage;
        }
        
        public String getDescription() {
            return beverage.getDescription() + ", Mocha";
        }
        
        public double getCost() {
            return beverage.getCost() + 0.2;
        }
    }
    
    /**
     * 客户端
     */
    public class Client {
        public static void main(String[] args) {
            Beverage expresso = new Espresso();
            Beverage expressoWithMocha = new Mocha(expresso);
            Beverage expressoWithDoubleMocha = new Mocha(expresso);
        }
    }
    

    原料价格改变不会影响到具体的咖啡类型; 出现新的调料只需要增加新的装饰者实现类即可; 调料也是按客户需求增加的, 不会增加多的调料.

  2. 装饰者模式定义:
    Attach additional responsibilities to an object dynamiclly keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality(动态地给一个都西昂添加一些额外的职责. 就增加功能来说, 装饰者模式相比生成子类更为灵活).

    装饰者模式一般用来扩展类的功能, 或者为类增加新的功能. 通过装饰者模式给类增加功能和通过继承给类增加功能的区别在于: 装饰者模式给类增加功能具有动态性.

    decorator

  3. 装饰者模式的应用:
    Java 的 I/O 接口是装饰者模式的典型应用.
    InputStream 对应装饰者模式的 Component, FileInputStream, StringBufferInputStream, ByteArrayInputStream 对应装饰者模式的 ConcreteComponent;

    FilterInputStream 对应装饰者模式的Decorator, PushBackInputStream, BufferedInputStream, DataInpustStream, LineNumberInputStream 对应装饰者模式的ConcreteDecorator.

    decorator-io

  4. 参考:
    [1] : Java 库中的设计模式
    [2] : 设计模式之禅
    [3] : Head First 设计模式