`

设计模式之: Decorator(装饰器)模式

阅读更多

 

转自:http://blog.csdn.net/eric_sunah/article/details/10164707

在说明什么是Decorator模式之前,先来看看它有什么优点,通过下面的例子你或许会对它有一个简单的认识

 

 

需求背景

 

 

设计一个Modem(调制解调器)的层次结构,在这个结构中

(1) Modem基类包含了一些调制解调器常用的功能,比如拨号,音量的控制

(2) 子类一:LoudModem,一般的拨号器在拨号的时候是没有声音的,这种modem在拨号的时候会发出声音

(3) 子类二:ScreenModem,一般的拨号器在拨号的时候是不会把号码显示在屏幕上的,这种modem在拨号的时候会将号码显示在屏幕上

 

 

方案一:继承

 

 

这是一种比较容易想到的方案,对于简单且稳定的业务场景这或许是个很好的选择,大概的类图如下

 



BaseModem实现了Modem接口中的方法,然后LoundModem,PrintModem都继承自BaseModem,根据需求分别覆盖dial方法

 

 

缺点

 

当子类的需求发生变化时,例如LoudModem也要求实现print的功能,这时只能修改LoudModem的dial方法,这时就违反了OCP(开放封闭原则)

如果有多个子类,每个子类都要求添加print的功能,那么这些子类的dial方法都需要进行修改,对于软件维护而言这或许是个噩梦的开始

方案二:Decorator模式

 

 

分析

将Modem的拨号特性 "Loud""Print" 都作为一个个单独的装饰类,当某个子类需要某个一些特性时,就直接用专门的装饰类来装饰它,且装饰的效果是可以叠加的.

例如 对于一个既有声音又能打印的modem而言,只需要用Loud以及Print这两个装饰器来装饰它既可,如果要想添加新的效果,只需要开发一个新的装饰器,然后用这个装饰器来装饰对应的子类便可,对于已经存在的代码不需要做任何的改动.

类图如下

 

从类图可以看出,每个Decorator都有一个dial方法,且都包含一个Modem的成员变量,具体的应用请参考下面的代码

样例代码

 

Modem接口类

 

  1. package com.eric.designmodel.decorator;  
  2.   
  3. public interface Modem {  
  4.     public void dial(String number);  
  5.       
  6.     public void setSpeakVolumn(int volumn);  
  7.       
  8.     public String getPhoneNumber();  
  9.       
  10.     public int getSpeakVolumn();  
  11. }  


BaseModem基类

 

 

  1. package com.eric.designmodel.decorator;  
  2.   
  3. public class BaseModem implements Modem {  
  4.       
  5.     private String  number;  
  6.     private int    volumn;  
  7.       
  8.     @Override  
  9.     public void dial(String number) {  
  10.         this.number = number;  
  11.     }  
  12.       
  13.     @Override  
  14.     public void setSpeakVolumn(int volumn) {  
  15.         this.volumn = volumn;  
  16.     }  
  17.       
  18.     @Override  
  19.     public String getPhoneNumber() {  
  20.         return number;  
  21.     }  
  22.       
  23.     @Override  
  24.     public int getSpeakVolumn() {  
  25.         return volumn;  
  26.     }  
  27.       
  28. }  


HayesModem 子类

 

 

  1. /** 
  2.  *  
  3.  */  
  4. package com.eric.designmodel.decorator;  
  5.   
  6. /** 
  7.  * Description: <br/> 
  8.  * Program Name:DesignPattern Date:2013-8-21 下午9:26:28 
  9.  *  
  10.  * @author Eric 
  11.  *  
  12.  * @version 1.0 
  13.  */  
  14. public class HayesModem extends BaseModem {  
  15.       
  16. }  


LoudDecorator装饰类

 

 

  1. package com.eric.designmodel.decorator;  
  2.   
  3. /** 
  4.  * Description:被该装饰器装饰过的Modem对象,在调用dial方法时, volumn会被设置为10<br/> 
  5.  * Program Name:DesignPattern Date:2013-8-21 下午9:36:45 
  6.  *  
  7.  * @author Eric 
  8.  *  
  9.  * @version 1.0 
  10.  */  
  11. public class LoudDecorator implements Modem {  
  12.       
  13.     private Modem   modem;  
  14.       
  15.     public LoudDecorator(Modem modem) {  
  16.         this.modem = modem;  
  17.     }  
  18.       
  19.     @Override  
  20.     public void dial(String number) {  
  21.         modem.setSpeakVolumn(10);  
  22.         modem.dial(number);  
  23.     }  
  24.       
  25.     @Override  
  26.     public void setSpeakVolumn(int volumn) {  
  27.         modem.setSpeakVolumn(volumn);  
  28.     }  
  29.       
  30.     @Override  
  31.     public String getPhoneNumber() {  
  32.         return modem.getPhoneNumber();  
  33.     }  
  34.       
  35.     @Override  
  36.     public int getSpeakVolumn() {  
  37.         return modem.getSpeakVolumn();  
  38.     }  
  39.       
  40. }  


ScreenDecorator装饰类

 

 

  1. package com.eric.designmodel.decorator;  
  2.   
  3. /** 
  4.  * Description: 被该装饰器装饰过的Modem对象,在调用dial方法时,number会加上区号<br/> 
  5.  * Program Name:DesignPattern Date:2013-8-21 下午9:35:01 
  6.  *  
  7.  * @author Eric 
  8.  *  
  9.  * @version 1.0 
  10.  */  
  11. public class ScreenDecorator implements Modem {  
  12.     public static final String  NUMBER_PREFIX   = "025+";  
  13.     private Modem              modem;  
  14.       
  15.     public ScreenDecorator(Modem modem) {  
  16.         this.modem = modem;  
  17.     }  
  18.       
  19.     @Override  
  20.     public void dial(String number) {  
  21.         modem.dial(NUMBER_PREFIX + number);  
  22.     }  
  23.       
  24.     @Override  
  25.     public void setSpeakVolumn(int volumn) {  
  26.         modem.setSpeakVolumn(volumn);  
  27.     }  
  28.       
  29.     @Override  
  30.     public String getPhoneNumber() {  
  31.         return modem.getPhoneNumber();  
  32.     }  
  33.       
  34.     @Override  
  35.     public int getSpeakVolumn() {  
  36.         return modem.getSpeakVolumn();  
  37.     }  
  38. }  


测试类

 

 

  1. package com.eric.designmodel.decorator;  
  2.   
  3. import junit.framework.TestCase;  
  4.   
  5. /** 
  6.  *  
  7.  * **/  
  8.   
  9. public class MainTest extends TestCase {  
  10.     private static final String NUMBER  = "10";  
  11.       
  12.     public void testNoneDecotarot() {  
  13.         Modem modem = new HayesModem();  
  14.         modem.dial(NUMBER);  
  15.         assertTrue(modem.getSpeakVolumn() == 0);  
  16.         assertTrue(modem.getPhoneNumber().equals(NUMBER));  
  17.     }  
  18.       
  19.     /** 
  20.      * 被Loud装饰器装饰过的Modem对象,在调用dial方法时, volumn会被设置为10 
  21.      */  
  22.     public void testLoudDecotarot() {  
  23.         Modem modem = new HayesModem();  
  24.         LoudDecorator loudModem = new LoudDecorator(modem);  
  25.         loudModem.dial(NUMBER);  
  26.         assertTrue(loudModem.getSpeakVolumn() == 10);  
  27.         assertTrue(loudModem.getPhoneNumber().equals(NUMBER));  
  28.           
  29.     }  
  30.       
  31.     /** 
  32.      * 被Screen装饰器装饰过的Modem对象,在调用dial方法时,number会加上区号 
  33.      */  
  34.     public void testScreenDecotarot() {  
  35.         Modem modem = new HayesModem();  
  36.         ScreenDecorator loudModem = new ScreenDecorator(modem);  
  37.         loudModem.dial(NUMBER);  
  38.         assertTrue(loudModem.getSpeakVolumn() == 0);  
  39.         assertTrue(loudModem.getPhoneNumber().startsWith(ScreenDecorator.NUMBER_PREFIX));  
  40.           
  41.     }  
  42.       
  43.     /** 
  44.      * 被Screen以及Loud装饰器装饰过的Modem对象,在调用dial方法时,number会加上区号并且volumn会被设置为10 
  45.      */  
  46.     public void testDoubleDecotarot() {  
  47.         Modem modem = new HayesModem();  
  48.         Modem screenModem = new ScreenDecorator(modem);  
  49.         Modem loudModem = new LoudDecorator(screenModem);  
  50.         loudModem.dial(NUMBER);  
  51.         assertTrue(loudModem.getSpeakVolumn() == 10);  
  52.         assertTrue(loudModem.getPhoneNumber().startsWith(ScreenDecorator.NUMBER_PREFIX));  
  53.     }  
  54. }  

 

优点

 

将每个特性都作为一个单独的装饰类,在需求发生变化的时候对特性进行动态的组合.且不需要修改具体的子类.

个人觉得该模式对比Proxy模式而言强大之处在于可以把多个装饰效果应用到某个子类中.

 

应用

 

 

Decorator模式java的stand lib中也被广泛的应用,例如: Java中的IO是明显的装饰器模式的运用。FilterInputStream,FilterOutputStream,FilterRead,FilterWriter分别为具体装饰器的父类,相当于Decorator类,它们分别实现了InputStream,OutputStream,Reader,Writer类(这些类相当于Component,是其他组件类的父类,也是Decorator类的父类)。继承自InputStream,OutputStream,Reader,Writer这四个类的其他类是具体的组件类,每个都有相应的功能,相当于ConcreteComponent类。而继承自FilterInputStream,FilterOutputStream,FilterRead,FilterWriter这四个类的其他类就是具体的装饰器对象类,即ConcreteDecorator类。通过这些装饰器类,可以给我们提供更加具体的有用的功能。如FileInputStream是InputStream的一个子类,从文件中读取数据流,BufferedInputStream是继承自FilterInputStream的具体的装饰器类,该类提供一个内存的缓冲区类保存输入流中的数据。我们使用如下的代码来使用BufferedInputStream装饰FileInputStream,就可以提供一个内存缓冲区来保存从文件中读取的输入流。

 

  1. BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); //其中file为某个具体文件的File或者FileDescription对象 
  2.    
分享到:
评论

相关推荐

    设计模式:可复用面向对象软件的基础--详细书签版

     “[设计模式]在实用环境下特别有用,因为它分类描述了一组设计良好,表达清楚的面向对象软件设计模式。整个设计模式领域还很新,本书的四位作者也许已占据了这个领域造诣最深的专家中的半数,因而他们定义模式的方法...

    分析Python中设计模式之Decorator装饰器模式的要点

    主要介绍了Python中设计模式之Decorator装饰器模式模式,文中详细地讲解了装饰对象的相关加锁问题,需要的朋友可以参考下

    jouryfirst#weekly-1#175.精读《设计模式 - Decorator 装饰器模式》1

    “就增加功能来说,Decorator 模式相比生成子类更为灵活” 这句话的含义是,组合比继承更灵活,当可拓展的功能很多时,继承方案会产生大量的子类,而组合可以提

    C#面向对象设计模式纵横谈(视频与源码)

    C#面向对象设计模式纵横谈(10):Decorator 装饰模式(结构型模式) C#面向对象设计模式纵横谈(11):Facade 外观模式(结构型模式) C#面向对象设计模式纵横谈(12):Flyweight 享元模式(结构型模式) C#面向对象设计...

    c++设计模式-结构型模式-装饰器模式

    c++设计模式-结构型模式-装饰器模式;QT工程;c++简单源码; 装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

    23种Python设计模式示例演示源码包.rar

    23种Python设计模式示例演示源码包,比如包括了工厂模式、Bridge桥接模式、Builder构建模式、Facade外观模式、Adapter适配器模式,Composite组合模式、Decorator装饰器模式,FactoryMethod工厂方法模式、Flyweight享...

    设计模式可复用面向对象软件的基础.zip

    书名: 设计模式可复用面向对象软件的基础 英文原书名: Design Patterns:Elements of Reusable Object-Oriented software 作者: Erich Gamma 等 译者: 李英军 马晓星 蔡敏 刘建中 书号: 7-111-07575-7 页码: 254 定价...

    java设计模式之装饰器模式(Decorator)

    主要为大家详细介绍了java设计模式之装饰器模式Decorator,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    设计模式_装饰器模式.zip

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。 这种模式创建了一个装饰类,用来包装原有的类,并在...

    Java设计模式之装饰模式(Decorator模式)介绍

    主要介绍了Java设计模式之装饰模式(Decorator模式)介绍,本文讲解了为什么使用Decorator、如何使用装饰模式、Jive中的Decorator实现等内容,需要的朋友可以参考下

    装饰器设计模式

    控制访问,装饰器设计的四个名词,装饰器模式与代理模式的区别,

    PHP设计模式(八)装饰器模式Decorator实例详解【结构型】

    主要介绍了PHP设计模式:装饰器模式Decorator,结合实例形式分析了PHP装饰器模式Decorator相关概念、功能、原理、用法及操作注意事项,需要的朋友可以参考下

    设计模式PPT

    创建型模式用来处理对象的创建过程,主要包含以下5种设计模式:  工厂方法模式(Factory Method Pattern)  抽象工厂模式(Abstract Factory Pattern)  建造者模式(Builder Pattern)  原型模式...

    C#设计模式.PDF

    C#设计模式(12)-Decorator Pattern 101 一、 装饰(Decorator)模式 101 二、 装饰模式的结构 102 三、 装饰模式示例性代码 103 四、 装饰模式应当在什么情况下使用 106 五、 装饰模式实际应用的例子 106 六、 ...

    C++设计模式(Design Pattern)范例源代码

    23种设计模式(Design Pattern)的C++实现范例,包括下面列出的各种模式,代码包含较详细注释。另外附上“设计模式迷你手册.chm” 供参考。 注:项目在 VS2008 下使用。 创建型: 抽象工厂模式(Abstract Factory) ...

    研磨设计模式(完整带书签).part2.pdf

    第22章 装饰模式(Decorator) 第23章 职责链模式(Chain of Responsibility) 第24章 桥接模式(Bridge) 第25章 访问者模式(Visitor) 附录A常见面向对象设计原则 附录BUML简介 参考文献

    PHP设计模式之装饰器模式定义与用法详解

    本文实例讲述了PHP设计模式之装饰器模式定义与用法。分享给大家供大家参考,具体如下: 什么是装饰器模式 作为一种结构型模式, 装饰器(Decorator)模式就是对一个已有结构增加”装饰”. 适配器模式, 是为现在有结构...

    33种JAVA设计模式DEMO

    装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) 3 行为型模式 这些设计模式特别关注对象之间的通信。 责任链模式(Chain of ...

    用Java实现23种设计模式

    装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) 3. 行为型模式 责任链模式(Chain of Responsibility Pattern) 命令模式(Command ...

Global site tag (gtag.js) - Google Analytics