装饰者模式

概念:动态的将责任附加到对象上。若要扩展该功能。装饰者提供了比继承更有弹性的替代方案。

要点:

继承属于扩展的形式之一,但不见得是达到弹性设计的最佳方案。

组合和委托可用于在运动的时候动态的加上新的行为。

装饰者一把对组件的客户是透明的,除非客户程序依赖于组件的具体实现。

装饰者会导致设计中出现许多小对象。如果使用过度,会让程序变得很复杂。

 

 

 

例子:

星巴兹咖啡准备更新订单系统,以合乎他们的饮料供应需求。

他们原先的类设计为:

这样的订单系统没有办法考虑到咖啡调料的部分,把加入不同调料的咖啡看做不同的类会导致类爆炸(每个类的cost方法计算出咖啡加调料的价钱):

很明显,这样的系统难以维护,一旦牛奶的价钱上扬或新增一种焦糖调料,系统将难以改变。

采用实例变量和继承的设计也许能解决一些问题:

Beverage作为一个饮料类,加上实例变量代表是否加入了饮料。

然而当用户想要双倍摩卡咖啡时,这样的系统就显得有些无所适从。

对于冰茶,饮料基类里的有些调料根本不适用,但是也一起继承了过来!

到目前为止,使用继承会造成的问题有:类爆炸,设计死板,以及基类加入的新功能并不适用于所有的子类。

所以继承并不是解决问题的方法,应当使用组合来使系统更有弹性且易于维护。

开放-关闭原则:

设计原则:

类应该对扩展开放,对修改关闭。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。

这个目标需要使用装饰着模式实现:以饮料为主体,然后运行调料来“装饰”饮料。

如图为一个摩卡和奶泡DarkRoast咖啡的设计图:

定义装饰者模式:

装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更具有弹性的替代方案。

装饰者模式类图:

现在让星巴兹咖啡系统也符合装饰者类图:

具体实现:

从饮料下手,将饮料作为一个抽象类:

 
  1. package com.cafe;  
  2.   
  3. public abstract class Beverage {  
  4.     String description = "Unknow Beverage";  
  5.   
  6.     public String getDescription() {  
  7.         return description;  
  8.     }  
  9.   
  10.     public abstract double cost();  
  11. }  

调料抽象类,也就是装饰者类:

 
  1. package com.cafe;  
  2.   
  3. public abstract class CondimentDecorator extends Beverage{  
  4.    public abstract String getDescription();  
  5. }  

实现具体的饮料(浓缩咖啡和综合咖啡):

 
  1. package com.cafe;  
  2.   
  3. public class Espresso extends Beverage {  
  4.     public Espresso() {  
  5.         description = "Espresso";  
  6.     }  
  7.   
  8.     public double cost() {  
  9.         return 1.99;  
  10.     }  
  11. }  
 
  1. package com.cafe;  
  2.   
  3. public class HouseBlend extends Beverage {  
  4.     public HouseBlend() {  
  5.         description = "HouseBlend";  
  6.     }  
  7.   
  8.     public double cost() {  
  9.         return 0.89;  
  10.     }  
  11. }  

实现具体装饰者类(摩卡)

 
  1. package com.cafe;  
  2.   
  3. public class Mocha extends CondimentDecorator{  
  4.     Beverage beverage;  
  5.     public Mocha(Beverage beverage)  
  6.     {  
  7.         this.beverage=beverage;  
  8.     }  
  9.     public String getDescription()  
  10.     {  
  11.         return beverage.getDescription()+", Mocha";  
  12.     }  
  13.     public double cost()  
  14.     {  
  15.         return 0.20+beverage.cost();  
  16.     }  
  17. }  

其他装饰者类的实现方式与摩卡类似。

测试代码:

 
  1. package com.cafe;  
  2.   
  3. public class StartbuzzCoffee {  
  4.     public static void main(String args[]) {  
  5.         Beverage beverage1 = new Espresso();  
  6.         System.out.println(beverage1.getDescription() + "  $"  
  7.                 + beverage1.cost());  
  8.   
  9.         Beverage beverage2 = new HouseBlend();  
  10.         beverage2 = new Soy(beverage2);  
  11.         beverage2 = new Mocha(beverage2);  
  12.         beverage2 = new Whip(beverage2);  
  13.         System.out.println(beverage2.getDescription() + "  $"  
  14.                 + beverage2.cost());  
  15.     }  
  16.   
  17. }  

测试结果:

 JAVA中的装饰者模式(java.io类):

Java I/O引出装饰者模式的一个“缺点”:利用装饰者模式,会造成设计中存在大量的小类。

编写自己的Java I/O装饰者,把输入流中的所有大写字母转成小写:

 
  1. package com.io;  
  2.   
  3. import java.io.*;  
  4.   
  5. public class InputTest {  
  6.     public static void main(String[] args) throws IOException {  
  7.         int c;  
  8.   
  9.         try {  
  10.             InputStream in = new LowerCaseInputStream(new BufferedInputStream(  
  11.                     new FileInputStream("D:\\test.txt")));  
  12.   
  13.             while ((c = in.read()) >= 0) {  
  14.                 System.out.print((char) c);  
  15.             }  
  16.   
  17.             in.close();  
  18.         } catch (IOException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.     }  
  22. }  

测试程序(测试刚刚写好的I/O装饰者)

 
  1. package com.io;  
  2.   
  3. import java.io.FilterInputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6.   
  7. public class LowerCaseInputStream extends FilterInputStream {  
  8.   
  9.     protected LowerCaseInputStream(InputStream in) {  
  10.         super(in);  
  11.         // TODO Auto-generated constructor stub  
  12.     }  
  13.   
  14.     public int read() throws IOException {  
  15.         int c = super.read();  
  16.         return (c == -1 ? c : Character.toLowerCase((char) c));  
  17.     }  
  18.   
  19.     public int read(byte[] b, int offset, int len) throws IOException {  
  20.         int result = super.read(b, offset, len);  
  21.         for (int i = offset; i < offset + result; i++) {  
  22.             b[i] = (byte) Character.toLowerCase((char) b[i]);  
  23.         }  
  24.         return result;  
  25.     }  
  26. }  

测试结果:

posted @ 2016-01-20 16:01  戎码一生  阅读(156)  评论(0)    收藏  举报