H__D  

装饰者模式

现在有这样一个项目,星巴克咖啡订单项目:

  1. 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)

  2. 调料:Milk、Soy(豆浆)、Chocolate

  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便

  4. 使用OO的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合

方案一(较差):

   

 方案一问题分析:

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

  2. des就是对咖啡的描述,比如咖啡的名字

  3. cost()方法就是计算费用,Drink类中做成一个抽象方法

  4. Decaf 就是单品咖啡,继承Drink,并实现cost

  5. Espress && Milk 就是单品咖啡+调料, 这个组合很多

  6. 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案二:

方案二问题分析:

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

  2. 在增加或者删除调料种类时,代码的维护量很大

  3. 考虑到用户可以添加多份调料时,可以将hasMilk 返回一个对应int

  4. 考虑使用装饰者模式

装饰者模式定义

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

  装饰者(Decorator)模式的主要优点有

  1. 采用装饰模式扩展对象的功能比采用继承方式更加灵活

  2. 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合

  其主要缺点是:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

装饰模式的结构与实现

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象

  2. 具体构件(Concrete    Component)角色:实现抽象构件,通过装饰角色为其添加一些职责

  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能

  4. 具体装饰(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 }

程序运行结果如下:

  1. 创建具体构件角色
  2. 调用具体构件角色的方法operation()
  3. ---------------------------------
  4. 调用具体构件角色的方法operation()
  5. 为具体构件角色增加额外的功能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 }

装饰模式的应用场景

  前面讲解了关于装饰模式的结构与特点,下面介绍其适用的应用场景,装饰模式通常在以下几种情况使用:

  1. 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类

  2. 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现

  3. 当对象的功能要求可以动态地添加,也可以再动态地撤销时

装饰者模式在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

posted on 2021-03-16 11:17  H__D  阅读(110)  评论(0编辑  收藏  举报