咖啡馆与装饰模式

需求

楼下的咖啡馆要开业了,我们看看他们主营的产品有哪些:

  1. 热牛奶
  2. 高乐高
  3. 摩卡
  4. 卡布奇诺

然后,咖啡馆的需求就来了:产品不同,售价也不同。同时作为咖啡馆,经常需要推出新品

思考

看到这个需求,第一反应是通过继承来解决这些问题。解决思路如下:

  1. 我们先定义一个饮料的基类
  2. 扩展这个基类,生成热牛奶、高乐高、摩卡等子类
  3. 通过工厂的方式返回这些子类(也就是生成饮料)。

这也算是一种中规中矩的思路了。但是有个问题,咖啡馆中的咖啡可能会随着市场经常调整,而且客户的口味有时候也是有变化的,比如都是摩卡,有的客户想要1份牛奶,但是有些客户想要2份牛奶。如果通过继承来解决这个问题,那就会衍生出超级多的子类。最后一方面程序会变得难以维护,另一方面产品的类型也会受到子类个数的限制。

那么我们应该用什么样的方式来处理更合适呢?答案就是装饰模式

装饰模式的定义

装饰模式是一种结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。就增加功能来说,相比继承的方式更加灵活。装饰模式看起来有点像俄罗斯套娃。

代码样例

接下来我们看看怎么在这个场景中应用装饰模式

原材料基类

首先,我们定义一下原材料的基类

/**
 * @author skykline
 * @date 2021/12/13
 **/
public abstract class MaterialAble {

    final MaterialAble materialAble;

    public MaterialAble(MaterialAble materialAble) {
        this.materialAble = materialAble;
    }

    /**
     * 材料列表
     * @return
     */
    public abstract String getMaterialList();

    /**
     * 计算费用
     * @return
     */
    public abstract double calcCost();
}

然后扩展一些我们会用到的原材料

这些原材料就可以被看成是一个一个的装饰器,用这些材料可以“装饰”其他的材料。

/**
 * 巧克力
 * @author skyline
 * @date 2021/12/13
 **/
public class Chocolate extends MaterialAble {

    public Chocolate(MaterialAble materialAble) {
        super(materialAble);
    }

    @Override
    public String getMaterialList() {
        return materialAble == null ? "巧克力" : materialAble.getMaterialList() + "," + "巧克力";
    }

    @Override
    public double calcCost() {
        return materialAble == null ? 1.3 : materialAble.calcCost() + 1.3;
    }
}
/**
 * 咖啡
 * @author skyline
 * @date 2021/12/13
 **/
public class Coffee extends MaterialAble {

    public Coffee(MaterialAble oriMaterial) {
        super(oriMaterial);
    }

    @Override
    public String getMaterialList() {
        return materialAble == null ? "咖啡" : materialAble.getMaterialList() + "," + "咖啡";
    }

    @Override
    public double calcCost() {
        return materialAble == null ? 1.1 : materialAble.calcCost() + 1.1;
    }
}
/**
 * 牛奶
 * @author skyline
 * @date 2021/12/13
 **/
public class Milk extends MaterialAble {

    public Milk(MaterialAble oriMaterial) {
        super(oriMaterial);
    }

    @Override
    public String getMaterialList() {
        return materialAble == null ? "牛奶" : materialAble.getMaterialList() + "," + "牛奶";
    }

    @Override
    public double calcCost() {
        return materialAble == null ? 1.5 : materialAble.calcCost() + 1.5;
    }
}
/**
 * 水
 * @author skyline
 * @date 2021/12/13
 **/
public class Water extends MaterialAble {

    public Water(MaterialAble oriMaterial) {
        super(oriMaterial);
    }

    @Override
    public String getMaterialList() {
        return materialAble == null ? "水" : materialAble.getMaterialList() + "," + "水";
    }

    @Override
    public double calcCost() {
        return materialAble == null ? 0.2 : materialAble.calcCost() + 0.2;
    }
}

最后,我们来生产咖啡吧

/**
 * @author skyline
 * @date 2021/12/13
 **/
public class Main {
    public static void main(String[] args) {
        System.out.println("来一份摩卡");
        MaterialAble mocha = createMocha();
        System.out.println("摩卡的成分列表:" + mocha.getMaterialList());
        System.out.println("摩卡的价格:" + mocha.calcCost());
        System.out.println("来一份高乐高");
        MaterialAble hotGaoLego = createHotGaoLego();
        System.out.println("高乐高的成分列表:" + hotGaoLego.getMaterialList());
        System.out.println("高乐高的价格:" + hotGaoLego.calcCost());
    }

    private static MaterialAble createHotGaoLego() {
        //首先,来一杯水
        MaterialAble gaoLego = new Water(null);
        //然后放点巧克力
        gaoLego = new Chocolate(gaoLego);
        //再来点牛奶
        gaoLego = new Milk(gaoLego);
        return gaoLego;
    }

    private static MaterialAble createMocha() {
        //首先,来一杯水
        MaterialAble mocha = new Water(null);
        //加入2份咖啡
        mocha = new Coffee(mocha);
        mocha = new Coffee(mocha);
        //再放入巧克力
        mocha = new Chocolate(mocha);
        return mocha;
    }
}

输出结果

来一份摩卡
摩卡的成分列表:水,咖啡,咖啡,巧克力
摩卡的价格:3.7
来一份高乐高
高乐高的成分列表:水,巧克力,牛奶
高乐高的价格:3.0

总结

  1. 在上面的例子中,我们虽然使用了继承,但是继承并不是解决问题的关键。使用继承是为了统一接口,因为在咖啡馆的这个例子中,产品可以是材料,材料也可以是产品。
  2. 在上面的例子中,每一种材料ChocolateCoffeeMilkWater它们既是材料,也是装饰器,并且,它们可以相互装饰。
  3. 解决问题的关键是我们使用了组合。将对象组合起来,产生新的对象。这样就非常灵活了。我们可以产生有两份咖啡的摩卡,也可以产生有两份巧克力的摩卡,最后价格可以根据使用的原材料来自动计算。
  4. 当某个原材料的价格修改时,如果我们使用继承来解决问题,那恐怕得修改很多很多的类,但是如果我们使用的是组合,我们不必修改所有的子类,只需要修改对应的原材料的类的价格即可
  5. 装饰器模式应用的重点在于:组合,动态的给对象添加一些职责或功能
  6. 装饰者可以在被装饰者的行为前/后加上自己的行为,以达到特定的目的
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
装饰者模式是一种结构型设计模式,它允许你动态地向对象添加行为。通过将对象放入装饰器类中,可以在运行时更改对象的行为。 在咖啡馆中,装饰者模式可以用于根据顾客的选择动态地添加配料并计算价格。我们可以创建一个基础咖啡类,然后创建多个装饰器类来添加不同的配料,并且每个装饰器类都可以计算自己的价格。 下面是一个简单的示例代码: ```java // 基础咖啡类 public abstract class Coffee { protected String description = "Unknown coffee"; public String getDescription() { return description; } public abstract double cost(); } // 浓缩咖啡类 public class Espresso extends Coffee { public Espresso() { description = "Espresso"; } public double cost() { return 1.99; } } // 装饰器类,用于添加牛奶 public class Milk extends Coffee { private Coffee coffee; public Milk(Coffee coffee) { this.coffee = coffee; } public String getDescription() { return coffee.getDescription() + ", Milk"; } public double cost() { return coffee.cost() + 0.5; } } // 装饰器类,用于添加摩卡 public class Mocha extends Coffee { private Coffee coffee; public Mocha(Coffee coffee) { this.coffee = coffee; } public String getDescription() { return coffee.getDescription() + ", Mocha"; } public double cost() { return coffee.cost() + 0.8; } } // 测试代码 public static void main(String[] args) { Coffee coffee = new Espresso(); // 创建一个浓缩咖啡 coffee = new Milk(coffee); // 添加牛奶 coffee = new Mocha(coffee); // 添加摩卡 System.out.println(coffee.getDescription() + " $" + coffee.cost()); } ``` 输出结果为:`Espresso, Milk, Mocha $3.29` 在这个例子中,我们创建了一个基础咖啡类 `Coffee`,并且创建了两个装饰器类 `Milk` 和 `Mocha`。我们可以通过创建不同的装饰器来添加不同的配料。最后,我们创建了一个浓缩咖啡对象 `Espresso`,并且动态地添加了牛奶和摩卡,最终计算出了价格。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值