`

设计模式之: 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.    
分享到:
评论

相关推荐

    C#设计模式之Decorator 装饰模式

    装饰模式(Decorator Pattern)是设计模式中的一种结构型模式,它在不改变原有对象的基础上,通过添加额外的职责来扩展对象的功能。在C#中,装饰模式尤其适用于那些需要动态地增加或减少对象功能的情况,避免了使用...

    设计模式之装饰模式(Decorator Pattern)

    装饰模式(Decorator Pattern)是一种结构型设计模式,它在不改变原有对象的基础上,通过包裹一个对象并为其添加新的行为或责任,实现对对象功能的扩展。这种模式在软件开发中非常常见,尤其当需要在运行时动态改变...

    c++-设计模式之装饰模式(Decorator)

    装饰模式(Decorator Pattern)是一种结构型设计模式,允许在不改变对象接口的情况下,动态地为对象添加额外的职责或功能。装饰模式通常用于需要扩展对象功能而又不希望使用子类化的场景。 装饰模式的组成 组件接口...

    装饰器模式(Decorator Pattern) 1. 装饰器模式简介 1.1 问题引入 1.2 装饰器模式的解决方案 2. 装饰器模式的定义和原理 2.1 定义 2.2 基本原理 2.3 装饰器模式

    装饰器模式(Decorator Pattern) 1. 装饰器模式简介 1.1 问题引入 1.2 装饰器模式的解决方案 2. 装饰器模式的定义和原理 2.1 定义 2.2 基本原理 2.3 装饰器模式的特点 3. 装饰器模式的UML类图和基本结构 3.1 UML...

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

    装饰器模式(Decorator Pattern)是一种结构型设计模式,主要用于在运行时动态地给对象添加新的职责或行为,而不必改变现有对象的类定义。在面向对象编程中,装饰器模式提供了一种相对于继承更加灵活的方式来增强或...

    装饰器(Decorator)模式

    装饰器(Decorator)模式 装饰器(Decorator)模式是一种典型的结构型模式,主要用意是动态地为对象添加一些额外...装饰器模式是一种非常实用的设计模式,在软件设计中可以广泛地应用于解决类库膨胀和维护的困难问题。

    设计模式C++学习之装饰模式(Decorator)

    装饰模式(Decorator)是软件设计领域中一种非常实用的结构型设计模式,它允许我们向一个对象添加新的行为或责任,而无需修改该对象的源代码。在C++编程语言中,装饰模式常用于动态地扩展类的功能,使得类的行为在...

    装饰器模式[Decorator]

    装饰器模式(Decorator)是一种设计模式,它允许在运行时向对象添加新的行为或责任,而无需修改对象的源代码。这种模式属于结构型模式,是面向对象设计中的一种非常实用的技术。 装饰器模式的核心思想是通过将一个...

    设计模式 t07Decorator

    在给定的“设计模式 t07Decorator”主题中,我们聚焦于装饰者模式(Decorator Pattern)。装饰者模式是一种结构型设计模式,它允许我们在运行时给对象添加新的行为或职责,而无需修改其原有代码。这种模式遵循开闭...

    设计模式之Decorator

    《设计模式之Decorator》 设计模式是软件工程中的一种最佳实践,它是在特定场景下解决常见问题的经验总结。Decorator模式是一种结构型设计模式,它的主要作用是为对象添加额外的功能,而无需修改对象的源代码。...

    [结构型模式] head first 设计模式之装饰者模式(decorator)

    装饰者模式(Decorator Pattern)是结构型设计模式之一,它允许在运行时向对象添加新的行为或职责,而无需修改对象的源代码。这个模式的名字来源于装饰艺术,它通过添加额外的装饰来增强一个物体的外观,同样地,...

    《设计模式:可复用面向对象软件的基础》英文版

    《设计模式:可复用面向对象软件的基础》是一本由Erich Gamma、Richard Helm等四位国际知名的软件工程师共同编写的经典之作,该书提供了面向对象软件设计中常用的模式,并通过具体的案例解释了这些模式如何帮助解决...

    JAVA设计模式学习12——装饰器模式

    装饰器模式是面向对象设计模式的一种,主要用于在不改变原有对象结构的情况下,动态地为对象增加新的功能。这种模式在Java中尤其常见,因为它允许我们遵循“开闭原则”——对扩展开放,对修改关闭。 装饰器模式的...

    设计模式-装饰器模式

    装饰器模式是一种结构型设计模式,它允许在不修改对象本身的情况下动态地为对象添加新的行为或职责。这种模式在软件工程中广泛应用,特别是在需要扩展已有功能而不影响原有代码结构时。在iOS开发中,装饰器模式同样...

    设计模式专题之(七)装饰模式---设计模式装饰模式示例代码(python--c++)

    在这个例子中,`my_decorator`是装饰器,它在调用原始函数`say_hello`前后分别添加了额外的行为。通过`@my_decorator`语法糖,我们方便地将`say_hello`函数装饰了。 接下来,我们看C++中的装饰模式实现,这通常涉及...

    java Decorator装饰模式例子

    装饰模式(Decorator Pattern)是设计模式中的一种结构型模式,它允许在运行时给对象添加新的行为或职责,而无需改变对象的类。在Java中,装饰模式通常通过继承和组合来实现,使得代码具有更好的扩展性和灵活性。...

    设计模式之装饰模式

    4. 具体装饰器(Concrete Decorator):实现了装饰器接口,并且持有具体组件的实例。它可以添加新的属性或行为,并调用具体组件的方法来实现装饰功能。 在C#代码示例中,我们可以创建一个`IComponent`接口,表示...

Global site tag (gtag.js) - Google Analytics