一、是什么?作用
.装饰器模式可以动态的将责任附加到对象上,若要扩展功能, 装饰者提供比继承更有弹性的替代方案
二、示例
1. 需求
有一杯焦糖奶茶要制作, 可以添加不同的配料,每种配料的价格不同,最后计算奶茶的价格
2. 原来的做法
1. 原来的做法,第一种最开始想到的, 首先建奶茶基类,然后新建焦糖奶茶类添加该配料的属性, 在价格方法判断是否有改配料,有的话则加上配料的钱。
2. 改进的第二种方法是,在建立一个奶茶的子类,添加一个List属性,用这个属性来存放不同的配料,在计算价格的方法里循环List循环加上配料的价格
总结:
- 第一种方法问题很大,如果以后需求变化回去修改子类的代码, 破坏了开闭原则, 如果搞个双份椰果,写起来很别扭
- 第二种方法, 面对配料类没问题, 如果需求在加个按大小杯加钱, 就需要修改子类的代码了,破坏了开闭原则
3. 用了模式的做法
1. 奶茶基类和子类
/** * 奶茶基类 */ public interface MilkTea { /** * 价格 */ float cost(); /** * 描述 */ String desc(); } // =================================== /** * 焦糖奶茶类 */ public class CaramelMilkTea implements MilkTea { @Override public float cost() { return 10f; } @Override public String desc() { return "焦糖奶茶!"; } }
2. 配料基类和子类
1 /** 2 * 配料基类 3 * 配料有很多种, 所以我们抽象出一个基类 4 */ 5 public abstract class CondimentDecorator implements MilkTea { 6 7 // 将顶层接口以构造参数的方式传递进来 8 private MilkTea milkTea; 9 10 public CondimentDecorator(MilkTea milkTea) { 11 this.milkTea = milkTea; 12 } 13 14 @Override 15 public float cost() { 16 return milkTea.cost(); 17 } 18 19 @Override 20 public String desc() { 21 return milkTea.desc(); 22 } 23 } 24 25 // ================= 26 27 /** 28 * 配料子类 - 布丁 29 */ 30 public class Pudding extends CondimentDecorator { 31 32 public Pudding(MilkTea milkTea) { 33 super(milkTea); 34 } 35 36 /** 37 * 重写描述, 告诉顾客加了布丁 38 */ 39 @Override 40 public String desc() { 41 return "布丁, " + super.desc(); 42 } 43 44 /** 45 * 重写价格方法, 在原来的价格上添加布丁的钱 46 */ 47 @Override 48 public float cost() { 49 return super.cost() + 0.5f; 50 } 51 }
3. 现在可以向顾客提供奶茶了, 测试类
/** * 测试主类 */ public class Main { public static void main(String[] args) { // 买一杯焦糖奶茶什么都不加 MilkTea milkTea = new CaramelMilkTea(); System.out.println("描述: " + milkTea.desc() + ", 价格: " + milkTea.cost()); // 用布丁装饰奶茶 MilkTea milkTea2 = new CaramelMilkTea(); milkTea2 = new Pudding(milkTea2); System.out.println("描述: " + milkTea2.desc() + ", 价格: " + milkTea2.cost()); // 我要一个双份布丁的奶茶, 再用一层布丁装饰奶茶 MilkTea milkTea3 = new CaramelMilkTea(); milkTea3 = new Pudding(new Pudding(milkTea3)); System.out.println("描述: " + milkTea3.desc() + ", 价格: " + milkTea3.cost()); // todo 如果以后再添加一个需求 每种奶茶有大杯, 中杯, 小杯需要加不同的钱, 只要新建一组杯子基类/ZI类, 重写描述和价格方法就可以了 } }
控制台显示
描述: 焦糖奶茶!, 价格: 10.0
描述: 布丁, 焦糖奶茶!, 价格: 10.5
描述: 布丁, 布丁, 焦糖奶茶!, 价格: 11.0
代码小结:
- 装饰着和杯装饰对象有相同的超类
- 可以用一个或者多个装饰者包装一个对象
- 装饰者可以在被装饰者的行为之前或者之后,加上自己的行为,以达到特定的目的
4. 找一个贴近实际的
1. 在java.io类中一系列的InputStream类
三、总结
1. 首先就是开闭原则,对扩展开放,对修改关闭
2. 组合和委托可用于在运行时动态的加上新的行为