定义

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

案例

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

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

image-20201006194448903.png

  1. //Drink,饮品抽象类,提取饮品的共同特点
  2. public abstract class Drink {
  3. private String desr;
  4. private float price = 0.0f;
  5. public String getDesr() {
  6. return desr;
  7. }
  8. public void setDesr(String desr) {
  9. this.desr = desr;
  10. }
  11. public float getPrice() {
  12. return price;
  13. }
  14. public void setPrice(float price) {
  15. this.price = price;
  16. }
  17. public abstract float cost(); //抽象的计算总价的方法
  18. }
  1. //咖啡抽象类,继承饮品类,是饮品的一个分支
  2. public abstract class Coffee extends Drink {
  3. @Override
  4. public float cost() {
  5. return super.getPrice();
  6. }
  7. }
  8. //咖啡实现类(具体的咖啡)
  9. //黑咖啡
  10. public class ShortBlack extends Coffee{
  11. public ShortBlack() {
  12. setDesr("黑咖啡");
  13. setPrice(4.0f);
  14. }
  15. }
  16. //美式咖啡
  17. public class American extends Coffee{
  18. public American() {
  19. setDesr("美式咖啡");
  20. setPrice(8.0f);
  21. }
  22. }
  1. //装饰类,本例中的是调料
  2. //注意继承Drink类
  3. public abstract class Decorator extends Drink {
  4. private Drink drink; //组合一个单品饮料
  5. public Decorator(Drink drink) {
  6. this.drink = drink;
  7. }
  8. @Override //重写计算总价的方法,加上调料的价钱
  9. public float cost() {
  10. return super.getPrice()+drink.cost();
  11. }
  12. @Override //重写饮品描述的方法
  13. public String getDesr() {
  14. return super.getDesr()+" "+super.getPrice()+" && "+drink.getDesr();
  15. }
  16. }
  17. //具体调料
  18. //巧克力调料
  19. public class Chocolate extends Decorator {
  20. public Chocolate(Drink drink) {
  21. super(drink);
  22. setDesr("巧克力");
  23. setPrice(1.5f);
  24. }
  25. }
  26. //牛奶调料
  27. public class Milk extends Decorator {
  28. public Milk(Drink drink) {
  29. super(drink);
  30. setDesr("牛奶");
  31. setPrice(1.0f);
  32. }
  33. }
  1. //客户类,测试
  2. public class Client {
  3. public static void main(String[] args) {
  4. //Drink drink = new ShortBlack();
  5. Drink drink = new American();
  6. System.out.print(drink.getDesr());
  7. System.out.println(" 总价"+drink.cost());
  8. drink = new Milk(drink); //单品美式+牛奶
  9. drink = new Chocolate(drink); //单品美式+牛奶+巧克力
  10. System.out.print(drink.getDesr());
  11. System.out.println(" 总价"+drink.cost());
  12. System.out.println(((Decorator)drink).getDrink().getDesr());
  13. drink = new Chocolate(drink); ////单品美式+牛奶+巧克力+巧克力
  14. System.out.print(drink.getDesr());
  15. System.out.println(" 总价"+drink.cost());
  16. System.out.println(
  17. ((Decorator)(((Decorator)drink).getDrink())).getDrink().getPrice()
  18. );
  19. }
  20. }

image-20201019103824866.png

装饰者模式解决星巴克咖啡订单

装饰者模式下的订单:2份巧克力+一份牛奶的LongBlackimage-20201006195901761.png
说明

  1. Milk包含了LongBlack
  2. 一份Chocolate包含了(Milk+LongBlack)
  3. 一份Chocolate包含了(Chocolate+Milk+LongBlack)

这样不管是什么形式的单品咖啡+调料组合,通过递归方式可以方便的组合和维护

个人总结
此模式适合某对象添加新功能(或者属性)后,顶层都为同一类的对象