好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

步步为营 .NET 设计模式学习笔记 十四、Decorator(装饰模式)

步步为营 .NET 设计模式学习笔记 十四、Decorator(装饰模式)

概述

在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?这就是本文要讲的Decorator模式。

一个场景是我们要为一个对象动态添加新的职责,这个职责并不修改原有的行为,而是在原有行为基础上添加新的功能,就好比装饰工人为一座新居的墙上涂抹上色彩缤纷的颜料一般。

意图

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。[GOF 《设计模式》]

<Design Pattern>结构图

图1 Decorator模式结构图

在装饰模式中的各个角色有:

抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。 具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。 具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任。

生活中的例子

装饰模式动态地给一个对象添加额外的职责。不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

图2 使用有画框的画作为例子的装饰模式对象图

示例用例图

在超市里,平常节日都有优惠活动,不同节日有不同优惠活动,我们用装饰模式写一个超市节日打折的例子,用例图如下:

代码设计

先创建SuperMarket.cs:

01 public   abstract   class   SuperMarket

02     {

03         private   int   _CategoryNumber;

04  

05         public   int   CategoryNumber

06         {

07             get   {  return   _CategoryNumber; }

08             set   { _CategoryNumber = value; }

09         }

10  

11         private   string   _Name;

12  

13         public   string   Name

14         {

15             get   {  return   _Name; }

16             set   { _Name = value; }

17         }

18  

19         private   double   _Discount;

20  

21         public   double   Discount

22         {

23             get   {  return   _Discount; }

24             set   { _Discount = value; }

25         }

26  

27         private   string   _Festival;

28  

29         public   string   Festival

30         {

31             get   {  return   _Festival; }

32             set   { _Festival = value; }

33         }

34  

35         public   abstract   string   ShowInfo();

36     }

再创建Decorator.cs:

01 public   abstract   class   Decorator : SuperMarket

02 {

03      protected   SuperMarket superMarker;

04  

05      public   Decorator(SuperMarket superMarker)

06      {

07          this .superMarker = superMarker;

08      }

09  

10      public   override   string   ShowInfo()

11      {

12          return   superMarker.ShowInfo();

13      }

14 }

再创建WalMart .cs:

01 public    class   WalMart:SuperMarket

02   {

03      public   WalMart()

04      {

05          this .CategoryNumber = 1000;

06          this .Discount = 0.95;

07          this .Name =  "沃尔玛" ;

08          this .Festival =  "平常" ;

09       

10      }

11       public   override   string   ShowInfo()

12       {

13           StringBuilder strBuilder =  new   StringBuilder();

14           strBuilder.AppendFormat( "在{0}超市,{1}节日期间,折扣为{2}的商品种类有{3}类.\n" ,  this .Name, this .Festival,  this .Discount,  this .CategoryNumber);

15           return   strBuilder.ToString();

16       }

17   }

1    

1 再创建MidAutumnFestival.cs:

01 public   class   MidAutumnFestival : Decorator

02 {

03      public   MidAutumnFestival(SuperMarket superMarket)

04          :  base (superMarket)

05      {

06  

07      }

08      public   void   Preferential()

09      {

10          superMarker.Discount = 0.95;

11          superMarker.CategoryNumber = 1500;

12          superMarker.Festival =  "中秋节" ;

13      }

14  

15      public   string   MoonCake()

16      {

17          return   "期间所有月饼8折优惠" ;

18      }

19 }

再创建SpringFestival.cs:

01 public   class   SpringFestival : Decorator

02 {

03      public   SpringFestival(SuperMarket supermarket)

04          :  base (supermarket)

05      {

06  

07      }

08  

09      public   void   Preferential()

10      {

11  

12          superMarker.Discount = 0.9;

13          superMarker.CategoryNumber = 2100;

14          superMarker.Festival =  "春节" ;

15      }

16  

17      public   string   GiftPreferential()

18      {

19          return   "期间有100种礼品类商品8折优惠." ;

20      }

21 }

最后再调用:

01 public   partial   class   Run : Form

02   {

03       public   Run()

04       {

05           InitializeComponent();

06       }

07  

08       private   void   btnRun_Click( object   sender, EventArgs e)

09       {

10       

11           //-------------------------------------

12  

13           SuperMarket superMarket =  new   WalMart();

14           rtbResult.AppendText(superMarket.ShowInfo()+ "\n" );

15           SpringFestival springFestival =  new   SpringFestival(superMarket);

16           springFestival.Preferential();

17           rtbResult.AppendText(springFestival.ShowInfo());

18           rtbResult.AppendText(springFestival.GiftPreferential()+ "\n\n" );

19           MidAutumnFestival midAutumnFestival =  new   MidAutumnFestival(superMarket);

20           midAutumnFestival.Preferential();

21           rtbResult.AppendText(midAutumnFestival.ShowInfo());

22           rtbResult.AppendText(midAutumnFestival.MoonCake() +  "\n\n" );

23  

24       }

25   }

结果如下图:

效果及实现要点

1.Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明,换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。

2.Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。

3.Decortor模式并非解决“多子类衍生的多继承”问题,Decorator模式的应用要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

4.对于Decorator模式在实际中的运用可以很灵活。如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以是ConcreteComponent的一个子类。

如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。

5.Decorator模式的优点是提供了比继承更加灵活的扩展,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。

6.由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

7.让装饰角色还继承抽象构件角色也是装饰模式最大的特点,目的就是给抽象构件增加职责,对外表现为装饰后的构件。

8.让装饰角色拥有构件角色实例的目的就是让构件能被多个装饰对象来装饰。

9.在具体应用中可以灵活一点,不一定要有抽象构件和装饰角色。但是,装饰对象继承装饰对象并且拥有它实例的两大特点需要体现。

10.透明装饰一般通过在基类方法前后进行扩充实现,半透明装饰一般通过新的接口实现。

适用性

1.需要扩展一个类的功能,或给一个类增加附加责任。

2.需要动态地给一个对象增加功能,这些功能可以再动态地撤销。

3.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。

4.从代码角度来说,如果你觉得由于功能的交叉扩展不会导致非常多的子类或者非常多的继承层次的话可以考虑装饰模式。

5.从应用角度来说,如果你希望动态给类赋予或撤销一些职责,并且可以任意排列组合这些职责的话可以使用装饰模式。

优点

1.装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

2.通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

3.这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错。

缺点

1.由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

注意点

1.一个装饰类的接口必须与被装饰类的接口相容。

2.尽量保持Component作为一个"轻"类,不要把太多的逻辑和状态放在Component类里。

3.如果只有一个ConcreteComponent类而没有抽象的Component类(接口),那么Decorator类经常可以是ConcreteComponent的一个子类。如下图所示:

4.如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。

总结

Decorator模式采用对象组合而非继承的手法,实现了在运行时动态的扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。同时它很好地符合面向对象设计原则中“优先使用对象组合而非继承”和“开放-封闭”原则。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于步步为营 .NET 设计模式学习笔记 十四、Decorator(装饰模式)的详细内容...

  阅读:33次

CopyRight:2016-2025好得很程序员自学网 备案ICP:湘ICP备09009000号-16 http://haodehen.cn
本站资讯不构成任何建议,仅限于个人分享,参考须谨慎!
本网站对有关资料所引致的错误、不确或遗漏,概不负任何法律责任。
本网站刊载的所有内容(包括但不仅限文字、图片、LOGO、音频、视频、软件、程序等)版权归原作者所有。任何单位或个人认为本网站中的内容可能涉嫌侵犯其知识产权或存在不实内容时,请及时通知本站,予以删除。

网站内容来源于网络分享,如有侵权发邮箱到:kenbest@126.com,收到邮件我们会即时下线处理。
网站框架支持:HDHCMS   51LA统计 百度统计
Copyright © 2018-2025 「好得很程序员自学网
[ SiteMap ]