咖啡馆与装饰模式

时间: 2023-12-16 admin IT培训

咖啡馆与装饰模式

咖啡馆与装饰模式

咖啡馆与装饰模式

  • 需求
  • 思考
  • 装饰模式的定义
  • 代码样例
    • 原材料基类
    • 然后扩展一些我们会用到的原材料
    • 最后,我们来生产咖啡吧
  • 总结

需求

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

  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);}@Overridepublic String getMaterialList() {return materialAble == null ? "巧克力" : materialAble.getMaterialList() + "," + "巧克力";}@Overridepublic 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);}@Overridepublic String getMaterialList() {return materialAble == null ? "咖啡" : materialAble.getMaterialList() + "," + "咖啡";}@Overridepublic 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);}@Overridepublic String getMaterialList() {return materialAble == null ? "牛奶" : materialAble.getMaterialList() + "," + "牛奶";}@Overridepublic 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);}@Overridepublic String getMaterialList() {return materialAble == null ? "水" : materialAble.getMaterialList() + "," + "水";}@Overridepublic 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. 装饰者可以在被装饰者的行为前/后加上自己的行为,以达到特定的目的