星巴克咖啡订单项目(咖啡馆)

  1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
  2. 调料:Milk、Soy(豆浆)、Chocolate
  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
  4. 使用OO的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。

    方案一实现

    其实这个就是穷举, 将所有的方式拼接
    image.png

    分析问题

  5. Drink是一个抽象类,表示饮料

  6. des就是对咖啡的描述,比如咖啡的名字
  7. cost()方法就是计算费用,Drink类中做成一个抽象方法.
  8. Decaf就是单品咖啡,继承Drink,并实现cost
  9. Espress&&Milk就是单品咖啡+调料,这个组合很多
  10. 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

    方案二实现

    前面分析到方案1因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目的维护性(如图)image.png
    说明:milk,soy,chocolate可以设计为Boolean,表示是否要添加相应的调料.

    分析问题

  11. 方案2可以控制类的数量,不至于造成很多的类

  12. 在增加或者删除调料种类时,代码的维护量很大
  13. 考虑到用户可以添加多份调料时,可以将hasMilk返回一个对应int
  14. 考虑使用装饰者模式

    装饰者模式定义

  15. 装饰者模式: 动态的将新功能附加到对象上, 在对象功能扩展方面, 它比继承更有弹性, 装饰者模式也体现了开闭原则

  16. 这里提到的动态的将新功能附加到对象和ocp原则, 在后面的应用实例上会以代码的形式体现

    装饰者模式原理

  • 装饰者模式就像打包一个快递
    • 主体:比如:陶瓷、衣服(Component)//被装饰者
    • 包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
  • Component主体:比如类似前面的Drink
  • ConcreteComponent和Decorator
    • ConcreteComponent:具体的主体,比如前面的各个单品咖啡
  • Decorator:装饰者,比如各调料.

在如图的Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。
image.png

装饰者模式解决方案

image.png

装饰者模式下的订单: 2份巧克力+一份牛奶的LongBlack

image.png

装饰者模式解决方案代码实现

  1. package com.flower.zhaungshizhe;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. public class TestMain {
  5. public static void main(String[] args) {
  6. // 装饰者模式下订单 2份巧克力 + 一份牛奶 + 一份LongBlank咖啡
  7. // 一份LongBlank咖啡
  8. Drink drink = new LongBlack();
  9. // 添加一份牛奶
  10. drink = new Milk(drink);
  11. // 添加一份巧克力
  12. drink = new Chocolate(drink);
  13. // 添加二份巧克力
  14. drink = new Chocolate(drink);
  15. float cost = drink.cost();
  16. System.out.println("价格为:" + cost);
  17. System.out.println(drink.getDes());
  18. }
  19. }
  20. /**
  21. * 饮用抽象类
  22. */
  23. @Data
  24. abstract class Drink{
  25. public String des;
  26. private float price = 0.0F;
  27. // 计算价格
  28. public abstract float cost();
  29. }
  30. /**
  31. * 咖啡类
  32. */
  33. class Coffee extends Drink{
  34. @Override
  35. public float cost() {
  36. return super.getPrice();
  37. }
  38. }
  39. /**
  40. * 意大利咖啡
  41. */
  42. class Espresso extends Coffee{
  43. public Espresso(){
  44. setDes("意大利咖啡");
  45. setPrice(6.0F);
  46. }
  47. }
  48. /**
  49. * 美式咖啡
  50. */
  51. class LongBlack extends Coffee{
  52. public LongBlack(){
  53. setDes("美式咖啡");
  54. setPrice(10.0F);
  55. }
  56. }
  57. /**
  58. * 装饰者
  59. */
  60. @AllArgsConstructor
  61. class Decorator extends Drink{
  62. // 被装饰者
  63. private Drink drink;
  64. @Override
  65. public float cost() {
  66. // 自身价格 + coffee价格
  67. return super.getPrice() + drink.cost();
  68. }
  69. @Override
  70. public String getDes() {
  71. return super.getDes() + " " + super.getPrice() + " && " + drink.getDes() + " " + drink.getPrice();
  72. }
  73. }
  74. /**
  75. * 巧克力
  76. */
  77. class Chocolate extends Decorator{
  78. public Chocolate(Drink drink) {
  79. super(drink);
  80. setDes("巧克力调味品");
  81. setPrice(3.0F);
  82. }
  83. }
  84. /**
  85. * 牛奶
  86. */
  87. class Milk extends Decorator{
  88. public Milk(Drink drink) {
  89. super(drink);
  90. setDes("牛奶调味品");
  91. setPrice(2.0F);
  92. }
  93. }

类图

image.png

源码剖析

JDK源码

Java的IO结构FilterInputStream就是一个装饰者
image.png
image.png