装饰者模式(Decorator Pattern)——1. 实现及

2017-12-18  本文已影响35人  Huangjs1994

写在前面

本文介绍装饰者模式,使用Java实现。首先以一个例子入手直接上手使用装饰者模式,然后再就原理进行讲解。

实例

下图是校园奶茶店的菜单,可以根据自己的需求在相应的饮料(Beverage)中添加调料(Condiment),例如可以选择奶茶+1份红豆+2份绿豆+3份冰块(16元)组合或者只要绿茶(3元)。现在该奶茶店由于生意太火爆需要定做一套自动化结算系统,该系统能够根据用户的选择结算相应的价格。

image.png
首先,分析菜单,可以看出饮料和调料都有描述(description)<描述指是什么饮料或调料>和单价,另外,调料还有一个份数(pieces)的属性。所以我们可以先抽象出来饮料类(Beverage)和调料类(Condiment)。Beverage有抽象方法getDescription()cost(),Codiment继承Beverage且有其独特的方法setPieces(),所有方法作用见注释,定义为抽象方法是为了让其子类必须实现它。
package pre.huangjs.decorator3;

public abstract class Beverage {

    // 返回对该饮料或者调料的描述
    public abstract String getDescription();
    
    // 返回价格
    public abstract double cost();
}
package pre.huangjs.decorator3;

public abstract class Condiment extends Beverage{
    
    // 设置需要的份数
    public abstract void setPieces(int pieces);
}

思考一下,继承Condiment需要实现几个方法? 【 getDescription() cost() setPieces() 】

奶茶+1份红豆+2份绿豆+3份冰块(16元)组合为例来构建我们的系统。首先我们先看看该组合的制作过程。

image.png

如图所示,

  1. 第一步我们需要一个奶茶对象
package pre.huangjs.decorator3;

public class MilkTea extends Beverage {

    @Override
    public String getDescription() {
        return "Milk Tea<5.0>";
    }

    @Override
    public double cost() {
        return 5.0;
    }

}
  1. 第二步,我们需要红豆(RedBeans)这个装饰者,用来把奶茶装饰为红豆奶茶,然后用红豆奶茶替换原先的奶茶,即将包装后的红豆奶茶赋值给图中变量beverage
package pre.huangjs.decorator3;

public class RedBeans extends Condiment {

    private int pieces; // 所需份数
    private Beverage beverage; // 装饰前的饮料
    
    /**
     * @param copies 需要的份数
     * @param beverage 待包装的饮料
     */
    public RedBeans(int copies, Beverage beverage) {
        setPieces(copies);
        this.beverage = beverage;
    }

    @Override
    public void setPieces(int pieces) {
        this.pieces = pieces;
    }

    @Override
    public String getDescription() {
        
        // 返回对装饰后饮料的描述,例如“奶茶+一份绿豆”的描述为“Milk Tea<5.0> + 1 pieces Red Beans<2.0 x 1>”
        return beverage.getDescription() + " + " + pieces + " pieces Red Beans<2.0 x " + pieces + ">";
    }

    @Override
    public double cost() {
        
        // 返回总计的费用 = 装饰前饮料的费用 + 一份绿豆的价格 x 所需的份数
        return beverage.cost() + 2.0 * pieces;
    }

}

RedBeans类中定义了成员变量Beverage beverage,这一变量的作用是记录装饰前饮料的状态,这样就可以在装饰前的基础上添加新功能。例如计算红豆奶茶总金额就是通过beverage.cost()回溯得到奶茶的价格,然后加上红豆的价格。

  1. 接下来两步需要构建绿豆(GreenBeans)装饰者和冰块(IceCubes)装饰者,思路和前一步一样,直接给出代码。
package pre.huangjs.decorator3;

public class GreenBeans extends Condiment {

    private int pieces;
    private Beverage beverage;
    
    public GreenBeans(int copies, Beverage beverage) {
        setPieces(copies);
        this.beverage = beverage;
    }

    @Override
    public void setPieces(int pieces) {
        this.pieces = pieces;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " + " + pieces + " pieces Green Beans<3.0 x " + pieces + ">";
    }

    @Override
    public double cost() {
        return beverage.cost() + 3 * pieces;
    }

}
package pre.huangjs.decorator3;

public class IceCubes extends Condiment {

    private int pieces;
    private Beverage beverage;

    public IceCubes(int pieces, Beverage beverage) {
        setPieces(pieces);
        this.beverage = beverage;
    }

    @Override
    public void setPieces(int pieces) {
        this.pieces = pieces;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " + " + pieces + " pieces Ice Cubes<1.0 x " + pieces + ">";
    }

    @Override
    public double cost() {
        return beverage.cost() + 1.0 * pieces;
    }

}

好了,我们可以来一杯奶茶+1份红豆+2份绿豆+3份冰块了。

package pre.huangjs.decorator3;

public class SchoolShopTest {

    public static void main(String[] args) {
        
        // 制作一杯奶茶
        Beverage beverage1 = new MilkTea();
        
        // 使用红豆来装饰奶茶
        beverage1 = new RedBeans(1, beverage1);
        
        // 使用绿豆来装饰红豆奶茶
        beverage1= new GreenBeans(2, beverage1);
        
        // 使用冰块来装饰绿豆红豆奶茶
        beverage1 = new IceCubes(3, beverage1);
        
        // 输出该饮料的描述
        System.out.println(beverage1.getDescription());
        
        // 输出该饮料的价格
        System.out.println("共计:" + beverage1.cost());
    }

}

结果为


image.png

这里我将GreenTea.javaBlackTea.java贴出来,可以参考。

package pre.huangjs.decorator3;

public class GreenTea extends Beverage {

    @Override
    public String getDescription() {
        return "Green Tea" + "<3.0>";
    }

    @Override
    public double cost() {
        return 3.0;
    }

}
package pre.huangjs.decorator3;

public class BlackTea extends Beverage{

    @Override
    public String getDescription() {
        return "Black Tea<3.0>";
    }

    @Override
    public double cost() {
        return 3.0;
    }

}

原理

结语

上一篇 下一篇

猜你喜欢

热点阅读