装饰者模式
现在有这样一个项目,星巴克咖啡订单项目:
-
咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
-
调料:Milk、Soy(豆浆)、Chocolate
-
要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
-
使用OO的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合
方案一(较差):
方案一问题分析:
-
Drink 是一个抽象类,表示饮料
-
des就是对咖啡的描述,比如咖啡的名字
-
cost()方法就是计算费用,Drink类中做成一个抽象方法
-
Decaf 就是单品咖啡,继承Drink,并实现cost
-
Espress && Milk 就是单品咖啡+调料, 这个组合很多
-
问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸
方案二:
方案二问题分析:
-
方案2可以控制类的数量,不至于造成很多的类
-
在增加或者删除调料种类时,代码的维护量很大
-
考虑到用户可以添加多份调料时,可以将hasMilk 返回一个对应int
-
考虑使用装饰者模式
装饰者模式定义
装饰者模式:指在不改变现有对象结构的情况下,动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)。
装饰者(Decorator)模式的主要优点有:
-
采用装饰模式扩展对象的功能比采用继承方式更加灵活
-
可以设计出多个不同的具体装饰类,创造出多个不同行为的组合
其主要缺点是:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
装饰模式的结构与实现
-
抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象
-
具体构件(Concrete Component)角色:实现抽象构件,通过装饰角色为其添加一些职责
-
抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能
-
具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任
模式的实现:
1 package decorator; 2 3 public class DecoratorPattern { 4 5 public static void main(String[] args) { 6 Component p = new ConcreteComponent(); 7 p.operation(); 8 System.out.println("---------------------------------"); 9 Component d = new ConcreteDecorator(p); 10 d.operation(); 11 } 12 } 13 14 15 // 抽象构件角色 16 interface Component { 17 public void operation(); 18 } 19 20 21 // 具体构件角色 22 class ConcreteComponent implements Component { 23 24 public ConcreteComponent() { 25 System.out.println("创建具体构件角色"); 26 } 27 28 public void operation() { 29 System.out.println("调用具体构件角色的方法operation()"); 30 } 31 } 32 33 34 // 抽象装饰角色 35 class Decorator implements Component { 36 37 private Component component; 38 39 public Decorator(Component component) { 40 this.component = component; 41 } 42 43 public void operation() { 44 component.operation(); 45 } 46 } 47 48 49 // 具体装饰角色 50 class ConcreteDecorator extends Decorator { 51 52 public ConcreteDecorator(Component component) { 53 super(component); 54 } 55 56 public void operation() { 57 super.operation(); 58 addedFunction(); 59 } 60 61 public void addedFunction() { 62 System.out.println("为具体构件角色增加额外的功能addedFunction()"); 63 } 64 }
程序运行结果如下:
-
创建具体构件角色
-
调用具体构件角色的方法operation()
-
---------------------------------
-
调用具体构件角色的方法operation()
-
为具体构件角色增加额外的功能addedFunction()
设计模式应用实例
代码实现:
1 package com.atguigu.decorator; 2 3 public abstract class Drink { 4 5 // 描述 6 public String des; 7 private float price = 0.0f; 8 9 public String getDes() { 10 return des; 11 } 12 13 public void setDes(String des) { 14 this.des = des; 15 } 16 17 public float getPrice() { 18 return price; 19 } 20 21 public void setPrice(float price) { 22 this.price = price; 23 } 24 25 // 计算费用的抽象方法 26 // 子类来实现 27 public abstract float cost(); 28 29 } 30 31 32 package com.atguigu.decorator; 33 34 public class Decorator extends Drink { 35 36 private Drink obj; 37 38 // 组合 39 public Decorator(Drink obj) { 40 this.obj = obj; 41 } 42 43 @Override 44 public float cost() { 45 // getPrice 自己价格 46 return super.getPrice() + obj.cost(); 47 } 48 49 @Override 50 public String getDes() { 51 // obj.getDes() 输出被装饰者的信息 52 return des + " " + getPrice() + " && " + obj.getDes(); 53 } 54 55 } 56 57 58 package com.atguigu.decorator; 59 60 public class Coffee extends Drink { 61 62 @Override 63 public float cost() { 64 return super.getPrice(); 65 } 66 67 } 68 69 70 package com.atguigu.decorator; 71 72 // 具体的Decorator,这里就是调味品 73 public class Chocolate extends Decorator { 74 75 public Chocolate(Drink obj) { 76 super(obj); 77 setDes(" 巧克力 "); 78 // 调味品的价格 79 setPrice(3.0f); 80 } 81 82 } 83 84 85 package com.atguigu.decorator; 86 87 public class Milk extends Decorator { 88 89 public Milk(Drink obj) { 90 super(obj); 91 setDes(" 牛奶 "); 92 setPrice(2.0f); 93 } 94 95 } 96 97 98 package com.atguigu.decorator; 99 100 public class Soy extends Decorator{ 101 102 public Soy(Drink obj) { 103 super(obj); 104 setDes(" 豆浆 "); 105 setPrice(1.5f); 106 } 107 108 } 109 110 111 package com.atguigu.decorator; 112 113 public class DeCaf extends Coffee { 114 115 public DeCaf() { 116 setDes(" 无因咖啡 "); 117 setPrice(1.0f); 118 } 119 } 120 121 122 package com.atguigu.decorator; 123 124 public class Espresso extends Coffee { 125 126 public Espresso() { 127 setDes(" 意大利咖啡 "); 128 setPrice(6.0f); 129 } 130 } 131 132 133 package com.atguigu.decorator; 134 135 public class LongBlack extends Coffee { 136 137 public LongBlack() { 138 setDes(" longblack "); 139 setPrice(5.0f); 140 } 141 } 142 143 144 package com.atguigu.decorator; 145 146 public class ShortBlack extends Coffee { 147 148 public ShortBlack() { 149 setDes(" shortblack "); 150 setPrice(4.0f); 151 } 152 } 153 154 155 package com.atguigu.decorator; 156 157 public class CoffeeBar { 158 159 public static void main(String[] args) { 160 161 // 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack 162 // 1. 点一份 LongBlack 163 Drink order = new LongBlack(); 164 System.out.println("费用 = " + order.cost()); 165 System.out.println("描述 = " + order.getDes()); 166 167 // 2. order 加入一份牛奶 168 order = new Milk(order); 169 170 System.out.println("order 加入一份牛奶 费用 = " + order.cost()); 171 System.out.println("order 加入一份牛奶 描述 = " + order.getDes()); 172 173 // 3. order 加入一份巧克力 174 order = new Chocolate(order); 175 176 System.out.println("order 加入一份牛奶 加入一份巧克力 费用 = " + order.cost()); 177 System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes()); 178 179 // 3. order 加入一份巧克力 180 order = new Chocolate(order); 181 182 System.out.println("order 加入一份牛奶 加入2份巧克力 费用 = " + order.cost()); 183 System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes()); 184 185 System.out.println("==========================="); 186 187 Drink order2 = new DeCaf(); 188 189 System.out.println("order2 无因咖啡 费用 = " + order2.cost()); 190 System.out.println("order2 无因咖啡 描述 = " + order2.getDes()); 191 192 order2 = new Milk(order2); 193 194 System.out.println("order2 无因咖啡 加入一份牛奶 费用 = " + order2.cost()); 195 System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes()); 196 197 } 198 199 }
装饰模式的应用场景
前面讲解了关于装饰模式的结构与特点,下面介绍其适用的应用场景,装饰模式通常在以下几种情况使用:
-
当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类
-
当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现
-
当对象的功能要求可以动态地添加,也可以再动态地撤销时
装饰者模式在JDK应用的源码分析
装饰模式在Java语言中的最著名的应用莫过于 Java I/O 标准库的设计了。例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。
代码示例:
1 package com.atguigu.jdk; 2 3 import java.io.DataInputStream; 4 import java.io.FileInputStream; 5 import java.io.InputStream; 6 7 public class Decorator { 8 9 public static void main(String[] args) throws Exception { 10 // 说明 11 // 1. InputStream 是抽象类, 类似我们前面讲的 Drink 12 // 2. FileInputStream 是 InputStream 子类,类似我们前面的 DeCaf, LongBlack 13 // 3. FilterInputStream 是 InputStream 子类:类似我们前面 的 Decorator 修饰者 14 // 4. DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 Milk, Soy 等 15 // 5. FilterInputStream 类 有 protected volatile InputStream in; 即含被装饰者 16 // 6. 分析得出在 jdk 的 io 体系中,就是使用装饰者模式 17 DataInputStream dis = new DataInputStream(new FileInputStream("d:\\abc.txt")); 18 System.out.println(dis.read()); 19 dis.close(); 20 } 21 }
装饰模式的扩展
装饰模式所包含的 4 个角色不是任何时候都要存在的,在有些应用环境下模式是可以简化的,如以下两种情况。
(1) 如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件,其结构图如图 4 所
(2) 如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并,其结构图如图 5 所示。
原文链接:https://blog.csdn.net/qq784515681/article/details/106225781