.NET简谈设计模式之(装饰者模式)

装饰者模式其实有点难以理解,特别是对初学者来讲可能有点晕,由于它的概念互相冲突,哪里互相冲突咱们下面会讲解到。编程

本人保持一向的写做风格,重在入门。在本人的这篇文章中会用一个比较恰当的比喻来让咱们对问题迎刃而解,例子虽然简单可是重点突出。设计模式

在写这篇文章以前我在网上大概搜了一下关于“装饰者模式”的一些文章,可是讲解的效果都不太理想。要么就是找书搬过来的,要么就是对着书的例子重新创造一个。我看了大概三四篇这样子,不行看着头晕。文章的主人很想把问题的关键说清楚,可是不多能在原有代码的基础上画龙点睛,搞很差就是多此一举。若是没能清楚的介绍模式代码中的任何一脚,都有可能給看文章的初学者带来新的问题,没可以透彻的体现出文章的重点。下面咱们从理清头绪开始。[王清培版权全部,转载请给出署名]架构

设计模式是用来解决某一个问题的一个方法,一个模式是对应着一个问题,好比观察者模式就是用来解决一对多的关系,这种关系是“牵一发而动全身”的做用。ide

咱们所看的设计模式书籍是一系列问题的集合,也是设计模式的集合。在咱们尚未能力将他们融会贯通以前,先单独理解这些思想。当咱们能驾驭这些设计模式以后,咱们就可以设计出不错的系统架构。模式之间是相通的,“设计原则”是引导模式创新的根本。书上的模式多数都是用来考虑一些小例子而已,若是用在真正的项目中,就须要结合整个设计模式的运用了。因此当咱们学习一些小的设计模式时,咱们不牵扯到其余的多余东西,先理解咱们当前模式的真正的思想是什么。学习

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

这是装饰者模式比较官方的定义,这句话咱们基本上都能理解其含义是什么。无非是动态的给要扩展的对象加上功能,不用继承而是用组合的方式。就是这句话给咱们初学者带来了第一个问题,是用组合而不是用继承来扩展对象的功能。咱们第一次接触装饰者模式的时候,就盯住了这句话,就是由于咱们盯住了这句话,因此咱们在下面的思考过程当中老是带着这个理论,因此老是会理解不了。朋友先不要记这个理论,先抛开不要记任何理论模型,我会用一个比喻来逐渐的让你理解装饰者模式真正的含义是什么。设计

请进入个人学习模式,在这里我打一个比喻;假如我家里如今要装修,要装修一个天花板上的灯。你们都知道天花板上的灯都是须要灯具进行装饰的,在这里咱们已经引入到了装饰的概念了,好咱们再来分析问题。那么灯具里面的灯泡是不变的,咱们又引入了以关键的概念,就是被装饰对象。灯泡是咱们一装修的时候就有的,外面的灯具是随时能够更换的,这里就造成了典型的装饰者模式的原型。请看图:[王清培版权全部,转载请给出署名]3d

1:对象

里面的灯泡就是被装饰者,外面的灯具就是装饰者。咱们已基本认识了装饰者模式的含义是什么了,下面咱们就用代码来进行模拟装饰者模式。blog

灯泡代码:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. namespace ConsoleApplication1  
  5. {  
  6.     public class 灯泡  
  7.     {  
  8.         public int 灯泡型号  
  9.         {  
  10.             get 
  11.             {  
  12.                 return 10;  
  13.             }  
  14.         }  
  15.         public string 点亮灯泡()  
  16.         {  
  17.             return "灯泡已经点亮";  
  18.         }  
  19.     }  
  20. }  

不要问为何灯泡类不是抽象的,在这里咱们不讨论其余的原理,咱们先理清装饰者模式在说,后面我会慢慢引入大家所迷惑的概念。

这是灯泡代码,里面很简单,一个表示灯泡型号的属性和一个点亮灯泡的方法。下面咱们来看装饰灯泡的灯具代码。

矩形灯具代码:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. namespace ConsoleApplication1  
  6. {  
  7.     public class 矩形灯具  
  8.     {  
  9.         /// <summary>    
  10.         /// 灯泡的引用    
  11.         /// </summary>    
  12.         private 灯泡 dengpaoobject;  
  13.         /// <summary>    
  14.         /// 既然是灯具装饰,那么必须能容纳灯泡,因此灯具必须引用灯泡    
  15.         /// </summary>    
  16.         /// <param name="d">灯泡的实例</param>    
  17.         public void 添加装饰的灯泡(灯泡 d)  
  18.         {  
  19.             dengpaoobject = d;  
  20.  
  21.         }  
  22.         /// <summary>    
  23.         /// 打开装饰了矩形灯具的感受    
  24.         /// </summary>    
  25.         /// <returns>添加装饰以后的效果</returns>    
  26.         public string 打开矩形灯具的效果()  
  27.         {  
  28.             return dengpaoobject.点亮灯泡() + ",矩形灯具所发出的光";  
  29.  
  30.         }  
  31.  
  32.     }  
  33.  
  34. }  
  35.  

多边形灯具代码:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. namespace ConsoleApplication1  
  6. {  
  7.     public class 多边形灯具  
  8.     {  
  9.         /// <summary>    
  10.         /// 灯泡的引用    
  11.         /// </summary>    
  12.         private 灯泡 dengpaoobject;  
  13.         /// <summary>    
  14.         /// 既然是灯具装饰,那么必须能容纳灯泡,因此灯具必须引用灯泡    
  15.         /// </summary>    
  16.         /// <param name="d">灯泡的实例</param>    
  17.         public void 添加装饰的灯泡(灯泡 d)  
  18.         {  
  19.             dengpaoobject = d;  
  20.         }  
  21.         /// <summary>    
  22.         /// 打开装饰了矩形灯具的感受    
  23.         /// </summary>    
  24.         /// <returns>添加装饰以后的效果</returns>    
  25.         public string 打开多边形灯具的效果()  
  26.         {  
  27.             return dengpaoobject.点亮灯泡() + ",多边形灯具所发出的光";  
  28.         }  
  29.  
  30.     }  
  31.  
  32. }  
  33.  

矩形阴影灯具:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. namespace ConsoleApplication1  
  6. {  
  7.     public class 矩形阴影灯具  
  8.     {  
  9.         /// <summary>    
  10.         /// 灯泡的引用    
  11.         /// </summary>    
  12.         private 灯泡 dengpaoobject;  
  13.         /// <summary>    
  14.         /// 既然是灯具装饰,那么必须能容纳灯泡,因此灯具必须引用灯泡    
  15.         /// </summary>    
  16.         /// <param name="d">灯泡的实例</param>    
  17.         public void 添加装饰的灯泡(灯泡 d)  
  18.         {  
  19.             dengpaoobject = d;  
  20.         }  
  21.         /// <summary>    
  22.         /// 打开装饰了矩形灯具的感受    
  23.         /// </summary>    
  24.         /// <returns>添加装饰以后的效果</returns>    
  25.         public string 打开多边形灯具的效果()  
  26.         {  
  27.             return dengpaoobject.点亮灯泡() + ",矩形阴影灯具所发出的光";  
  28.         }  
  29.  
  30.     }  
  31.  
  32. }  
  33.  
  34.  
  35.  

不要问为何装饰灯具没有继承上面的灯泡,我之因此这样讲解,就是为了绕开那个给咱们带来理解上困惑的陷阱。继续往下看就能完全明白了。

装饰者跟被装饰者之间没有任何继承关系,“装饰者模式”官方的解释就是这个意思,只是经过组合来扩展对象的职责。若是正常状况下,咱们确定是用继承来解决灯具的装饰问题,有多少了灯具都继承自灯泡类,可是经过这种包含的方式就绕开了继承带来的耦合问题。上面的代码还远远没有完成,咱们来看后面会出现什么样的问题。

 模式灯具的效果:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. namespace ConsoleApplication1  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             灯泡 dengpao = new 灯泡();  
  12.  
  13.             矩形灯具 juxingdengju = new 矩形灯具();  
  14.             juxingdengju.添加装饰的灯泡(dengpao);  
  15.             Console.WriteLine(juxingdengju.打开矩形灯具的效果());  
  16.             Console.ReadLine();  
  17.  
  18.             多边形灯具 duobianxingdengju = new 多边形灯具();  
  19.             duobianxingdengju.添加装饰的灯泡(dengpao);  
  20.             Console.WriteLine(duobianxingdengju.打开多边形灯具的效果());  
  21.             Console.ReadLine();  
  22.  
  23.             矩形阴影灯具 yinyingdengju = new 矩形阴影灯具();  
  24.             yinyingdengju.添加装饰的灯泡(dengpao);  
  25.             Console.WriteLine(yinyingdengju.打开多边形灯具的效果());  
  26.             Console.ReadLine();  
  27.         }  
  28.     }  
  29. }  

2:

从上面的灯具例子来看,咱们基本上完成了装饰者模式的模拟。可是这样的代码扩展性太差,设计模式所提倡面向接口编程,而咱们这里彷佛没有看见接口。例子中的全部代码都是直接使用对象的名称,这样确定是耦合的。咱们继续延伸问题,来解决你刚开始开文章的困境,抽象类、接口、继承跑哪去了。总以为例子的代码太简单了,哪像是设计模式啊。别急咱们继续讨论,设计模式原本就是思想性的理论,须要耐心的研究。

咱们的灯泡是实体类而不是抽象类,这就说明咱们的灯泡设计是不合理的,咱们假如灯泡是个半成品,这个半成品是不能直接用的,任何装饰者都须要通过必定的修改才能使用。因此这样一来,咱们的灯泡类就是抽象的了;

那为何须要用装饰者来继承被装饰者呢,其实很简单缘由就是没有统一的接口。咱们假如灯泡只能用一种方式打开,任何灯具都不能擅自修改这统一的接口。由于这接口是灯泡厂商规定的,好比一些电流、电压、线路原理等等都是不容许直接修改的。只有继承自装饰者对象我才可以使用被装饰者的约定,好比一个方法的名称、一个属性的名称。而咱们在使用的时候彻底是使用被装饰者的方式在使用,这样就破快了约定。

还有就是为何咱们没有用接口,咱们来延伸出接口的使用。假如一个灯具能够装饰不少种灯泡,那么必然就须要一个统一的接口来约束这些必备条件。 好比灯具只能装饰多大的灯泡、什么型号的灯泡等等;

上面的几个问题,我就大概的描述了一下。下面咱们来对代码进行一些修改,让它看起来像“装饰者模式”。

更改后的灯泡代码:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. namespace ConsoleApplication1  
  6. {  
  7.     public interface 灯泡系列  
  8.     {  
  9.         int 灯泡型号 { get; }  
  10.         string 打开灯();  
  11.     }  
  12.     public abstract class 灯泡 : 灯泡系列  
  13.     {  
  14.         public int 灯泡型号  
  15.         {  
  16.             get { return 10; }  
  17.         }  
  18.         public virtual string 打开灯()  
  19.         {  
  20.             return "灯泡已经点亮";  
  21.         }  
  22.     }  
  23.     public class 红色灯泡 : 灯泡  
  24.     {  
  25.         public int 灯泡型号  
  26.         {  
  27.             get { return 10; }  
  28.         }  
  29.         public override string 打开灯()  
  30.         {  
  31.             return "红色灯泡";  
  32.         }  
  33.     }  

更改后的矩形灯具代码:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. namespace ConsoleApplication1  
  6. {  
  7.     public class 矩形灯具 : 灯泡  
  8.     {  
  9.         /// <summary>  
  10.         /// 灯泡的引用  
  11.         /// </summary>  
  12.         private 灯泡系列 dengpaoobject;  
  13.         /// <summary>  
  14.         /// 既然是灯具装饰,那么必须能容纳灯泡,因此灯具必须引用灯泡  
  15.         /// </summary>  
  16.         /// <param name="d">灯泡的实例</param>  
  17.         public void 添加装饰的灯泡(灯泡 d)  
  18.         {  
  19.             dengpaoobject = d;  
  20.         }  
  21.         public override string 打开灯()  
  22.         {  
  23.             return dengpaoobject.打开灯() + ",外加矩形灯具效果";  
  24.         }  
  25.     }  
  26. }  

其余几个灯具代码都同样的我就不贴出来了。

调用代码:

  
  
  
  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.  
  5. namespace ConsoleApplication1  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             红色灯泡 reddengpao = new 红色灯泡();//灯泡  
  12.  
  13.             矩形灯具 juxingdengju = new 矩形灯具();//装饰  
  14.  
  15.             多边形灯具 duobianxingdengju = new 多边形灯具();//装饰  
  16.  
  17.             duobianxingdengju.添加装饰的灯泡(reddengpao);  
  18.  
  19.             juxingdengju.添加装饰的灯泡(duobianxingdengju);  
  20.  
  21.  
  22.             Console.WriteLine(juxingdengju.打开灯());  
  23.             Console.ReadLine();  
  24.  
  25.         }  
  26.     }  
  27. }  

3:

装饰者模式就讲的差很少了。这里面用到了接口、继承、多态等特性,其实接口就是用来消除类之间的耦合,继承是为了拿对象的行为,多态是为了将职责动态的添加到灯具中去。模式中的一些特性是会随着环境的不一样而不一样,有些时候根本不须要接口,可是为了进行多类型之间的扩展就必须进行接口编程。

总结:我我的以为装饰者模式用的场合不是不少,继承就是为了拿对象的行为,而后在单独引用一个对象的实例,这样就等于浪费了一个对象的内存。简单的装饰者能够不用继承,若是须要统一调用的话就须要继承了,接口只是用来表示装饰者不只仅能够装饰某一个对象,而是某一类对象。根据须要的不一样模式能够适当的进行修改,以适应当前环境。

相关文章
相关标签/搜索