知识大全 C# Design Patterns (3) - Decorator

Posted

篇首语:知识像烛光,能照亮一个人,也能照亮无数的人。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 C# Design Patterns (3) - Decorator相关的知识,希望对你有一定的参考价值。

C# Design Patterns (3) - Decorator  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  Decorator Pattern (装饰模式)

  装饰模式可「动态」地给一个对象添加一些额外的职责 提供有别于「继承」的另一种选择 就扩展功能而言 Decorator Pattern 透过 Aggregation (聚合) 的特殊应用 降低了类与类之间的耦合度 会比单独使用「继承」生成子类更为灵活

  一般用「继承」来设计子类的做法 会让程序变得较僵硬 其对象的行为 是在「编译」时期就已经「静态」决定的 而且所有的子类 都会继承到相同的行为 然而 若用「装饰模式」以及 UML 的 Aggregation 的设计 来扩展对象的行为 就能弹性地 (flexible) 将多个「装饰者」混合著搭配使用 而且是在「执行」时期「动态」地进行扩展

  此外 若用一般「继承」的做法 每当对象需要新行为时 必须修改既有的代码 重新编译 但若透过「装饰模式」 则无须修改既有代码

  The Decorator Pattern attaches additional responsibilities to an object dynamically Decorators provide a flexible alternative to subclassing for extending functionality

  - Design Patterns: Elements of Reusable Object Oriented Sofare

  

  图  此图为 Decorator 模式的经典 Class Diagram

   _Shell / Program cs

  using System;

  namespace _ _Shell

  

  //客户端程序

  class Program

  

  static void Main(string[] args)

  

  ConcreteComponent c = new ConcreteComponent();

  ConcreteDecoratorA d = new ConcreteDecoratorA();

  ConcreteDecoratorB d = new ConcreteDecoratorB();

  //让 ConcreteDecorator 在运行时 动态地给 ConcreteComponent 增加职责

  d SetComponent(c);

  d SetComponent(d );

  d Operation();

  Console Read();

  

  

  //装饰者 被装饰者的共同基类

  abstract class Component

  

  public abstract void Operation();

  

  //被装饰者 具体被装饰对象

  //此为在「执行」时期 会被 ConcreteDecorator 具体装饰者 「动态」添加新行为(职责)的对象

  class ConcreteComponent : Component

  

  public override void Operation()

  

  Console WriteLine( 具体被装饰对象的操作 );

  

  

  //装饰者

  //此为所有装饰者 应共同实现的抽象类或接口

  abstract class Decorator : Component

  

  //以父类声明的字段

  //实现 UML Class Diagram 的 Aggregation 指向 Component 对象的指针

  protected Component ponent;

  public void SetComponent(Component ponent)

  

  ponent = ponent;

  

  public override void Operation()

  

  if (ponent != null)

  

  ponent Operation();

  

  

  

  //具体装饰者 A

  //此为在「执行」时期 替 ConcreteComponent「动态」添加新行为(职责)的对象

  class ConcreteDecoratorA : Decorator

  

  //装饰者可以添加新的栏位

  private string addedState;

  public override void Operation()

  

  base Operation();

  addedState = New State ;

  Console WriteLine( 具体装饰对象 A 的操作 );

  

  

  //具体装饰者 B

  //此为在「执行」时期 替 ConcreteComponent「动态」添加新行为(职责)的对象

  class ConcreteDecoratorB : Decorator

  

  public override void Operation()

  

  base Operation();

  AddedBehavior();

  Console WriteLine( 具体装饰对象 B 的操作 );

  

  //装饰者可以添加新的方法

  private void AddedBehavior()

  

  

  

   // end of namespace

  /*

  执行结果

  具体被装饰对象的操作

  具体装饰对象 A 的操作

  具体装饰对象 B 的操作

  */

  上方图 的 Class Diagram 以及「Shell (壳)」示例中 ConcreteComponent 即为此一模式中的「被装饰者」 而 Decorator 抽象类及其具体子类 ConcreteDecoratorA ConcreteDecoratorB 即为「装饰者」 此外 这个模式中最核心的就是 Decorator 这个抽象类 它用一个以父类 Component 声明的变量 ponent 实现 UML 中的 Aggregation (聚合 有的博文也统称为「组合」或「合成」) 亦即为指向 Component 对象的指针 达到我们前述 「动态」进行扩展的目的

  至于其设计的原理 以下我引用博客园一位前辈 Justin 两年前所写博文的一段内容 [ ] 这段是我认为对 Decorator 模式极优的说明 读者亦可搭配参考上方代码里我添加的注释 稍后我会再补充说明

  

  Decorator 是装饰者模式里非常特殊的一个类 它既继承于 Component【IS A关系】 又维护一个指向 Component 实例的引用【HAS A关系】 换个角度来说 Decorator 跟 Component 之间 既有动态组合关系 又有静态继承关系 WHY? 这里为什么要这么来设计?上面我们说过 组合的好处是可以在运行时给对象增加职责 Decorator【HAS A】Component 的目的 是让 ConcreteDecorator 可以在运行时动态地给 ConcreteComponent 增加职责 这一点相对来说还比较好理解 那么 Decorator 继承于 Component 的目的是什么?在这里继承的目的只有一个 那就是可以统一「装饰者」和「被装饰者」的接口 换个角度来说 不管是 ConcretComponent 还是 ConcreteDecorator 它们都是最顶层 Component 基类 用户代码可以把它们统一看作 Component 来处理 这样带来的更深一层好处就是 「装饰者」对象 对「被装饰者」对象的功能职责扩展 对用户代码来说是完全透明的 因为用户代码引用的都是 Component 所以就不会因为「被装饰者」对象在被装饰后 引用它的用户代码发生错误 实际上不会有任何影响 因为装饰前后 用户代码引用的都是 Component 类型的对象 这真是太完美了!「装饰模式」通过继承 实现统一了「装饰者」和「被装饰者」的接口 通过组合获得了在运行时动态扩展「被装饰者」对象的能力

  我们再举个生活中的例子 俗话说 人在衣着马在鞍 把这用「装饰模式」的语境翻译一下 人通过漂亮的衣服装饰后 男人变帅了 女人变漂亮了 对应上面的类图 这里「人」对应于 ConcreteComponent 而「漂亮衣服」则对应于 ConcreteDecorator 换个角度来说 人和漂亮衣服组合在一起【HAS A】 有了帅哥或美女 但是他们还是人【IS A】 还要做人该做的事情 但是可能会对异性更有吸引力了(扩展功能)!

  

  上方 Justin 前辈 其文章的「煮咖啡」示例 [ ] 是引用自 O Reilly 出版社的「Head First 设计模式」这本书的第三章 [ ] 该文的煮咖啡类图中 HouseBlend (家常咖啡) DarkRoast (深度烘培咖啡) Espresso (意大利特浓咖啡) Decaf (无咖啡因咖啡) 这四种咖啡 (饮料) 即为「被装饰者」 等同本帖上图 中的 ConcreteComponent 类 该文类图中的 CondimentDecorator 抽象类 等同本帖上图 中最重要的 Decorator 抽象类 亦即「装饰者」的抽象定义 该文类图中的 Milk Mocha Soy Whip 这四种调料 (调味品) 即为具体的「装饰者」 亦即在本帖一开始提到 这四种调料 可弹性地 (flexible) 混合著搭配使用 而且是在「执行」时期「动态」地进行扩展 亦即动态地装饰 HouseBlend DarkRoas Espresso Decaf 这四种咖啡

  接下来 我们用另一个简单的例子来实现 Decorator 模式 并改用 ASP NET 网页程序来实现 类图及代码如下方图 所示 执行结果如下图 所示

  此为一个西餐牛排馆的计费程序 这间牛排馆有两种主菜 牛排和猪排 此为「被装饰者」 有四种副菜 面条 生菜沙拉 饮料 (热饮或冷饮) 甜点 此为「装饰者」 客人点餐时 可点一种主菜 副菜可点一份 可点多份 也可不点 (有弹性地将多个「装饰者」混合著搭配) 每样主菜和副菜都有各自的价格 全部相加后 即为一或多位客人的用餐费用 而且我们希望 将来这个计费程序写好后 未来即使要修改价格 或添加新的主菜和副菜时 都不用再修改既有的程序

  

  图  示例 _Steak aspx cs 的 Class Diagram

   _Steak aspx cs

  using System;

  using blogs WizardWu sample ;       //客户端程序调用服务器端的组件和类

  //客户端程序

  public partial class _ _Steak : System Web UI Page

  

  protected void Page_Load(object sender EventArgs e)

  

  //点一客猪排 不需要副菜 并列出它的描述与价格

  Meal meal = new PorkChop();

  //Meal meal = new BeefSteak();

  Response Write(meal Description + = $ + st() +

   );

  //点一客猪排 副菜只要面条 并列出它的描述与价格

  Meal meal = new PorkChop();

  meal = new Noodle(meal );          //用 Noodle 装饰它 (运行时动态地增加职责)

  Response Write(meal Description + = $ + st() +

   );

  //点一客猪排 因为这个人食量大 副菜要两份面条 一杯饮料(热饮) 一盘甜点 并列出它的描述与价格

  Meal meal = new PorkChop();

  meal = new Noodle(meal );          //用 Noodle 装饰它

  meal = new Noodle(meal );          //用第二个 Noodle 装饰它

  meal = new Drink(meal true);    //用 Drink 装饰它

  meal = new Dessert(meal );         //用 Dessert 装饰它

  Response Write(meal Description + = $ + st() +

   );

  //第四个人不吃猪肉 因此主菜改点一客牛排 副菜要一盘沙拉 一杯饮料(冷饮) 并列出它的描述与价格

  Meal meal = new BeefSteak();

  meal = new Salad(meal );            //用 Salad 装饰它 (运行时动态地增加职责)

  meal = new Drink(meal false);   //用 Drink 装饰它

  Response Write(meal Description + = $ + st() +

   );

  

  

  //服务器端程序

  namespace blogs WizardWu sample

  

  //装饰者(副菜) 被装饰者(主菜)的共同基类 类似前一例的 Component 抽象类

  abstract class Meal

  

  protected string description = 西餐 ;

  public virtual string Description

  

  get return description;

  set description = value;

  

  abstract public int cost();        //必须在子类实作的抽象方法 除非该个子类也是抽象类

  

  //主菜(被装饰者) 类似前一例的 ConcreteComponent 类

  class PorkChop : Meal                //主菜 猪排 以后执行时 才会动态被副菜装饰

  

  public PorkChop()                  //构造函数

  

  Description = 猪排 ;

  

  public override int cost()         //具体对象的操作

  

  return ;                        //猪排的价格 现在不需要管「副菜」的价格 直接返回猪排的价格即可

  

  

  //主菜(被装饰者) 类似前一例的 ConcreteComponent 类

  class BeefSteak : Meal               //主菜 牛排 以后执行时 才会动态被副菜装饰

  

  public BeefSteak()                 //构造函数

  

  Description = 牛排 ;

  

  public override int cost()         //具体对象的操作

  

  return ;                        //牛排的价格 现在不需要管「副菜」的价格 直接返回牛排的价格即可

  

  

  //装饰者 类似前一例的 Decorator 抽象类 此为所有「副菜(装饰者)」的父类

  //功能一 未来所有可能添加的副菜 其共同的属性 行为 都可放在这个类里

  //功能二 用来「分类」哪些是主菜 哪些是副菜

  abstract class CondimentDecorator : Meal      //副菜 用来「动态」装饰主菜和 Meal 基类 亦即在运行时动态地替他们增加职责

  

  //以父类声明的字段

  //实现 UML Class Diagram 的 Aggregation 指向 Meal 对象的指针

  protected Meal meal;

  public override string Description

  

  get return meal Description + + + description;

  set description = value;

  

  

  //副菜(装饰者) 具体装饰类 面条 用来动态装饰主菜 牛排或猪排

  class Noodle : CondimentDecorator            //类似前一例的 ConcreteDecoratorA

  

  public Noodle(Meal meal)        //构造函数

  

  Description = 面条 ;

  al = meal;

  

  public override int cost()         //具体装饰对象 A 的操作

  

  return + st();

  

  

  //副菜(装饰者) 具体装饰类 沙拉 用来动态装饰主菜 牛排或猪排

  class Salad : CondimentDecorator             //类似前一例的 ConcreteDecoratorB

  

  public Salad(Meal meal)          //构造函数

  

  Description = 生菜沙拉 ;

  al = meal;

  

  public override int cost()         //具体装饰对象 B 的操作

  

  return + st();

  

  

  //副菜(装饰者) 具体装饰类 饮料 用来动态装饰主菜 牛排或猪排

  class Drink : CondimentDecorator             //类似前一例的 ConcreteDecoratorC

  

  private bool isHot;

  public bool IsHot

  

  get return isHot;

  

  public Drink(Meal meal bool isHot)          //构造函数

  

  Description = (isHot) ? 热饮 : 冷饮 ;

  al = meal;

  this isHot = isHot;

  

  public override int cost()         //具体装饰对象 C 的操作 冷饮比热饮多收 块钱

  

  return (isHot) ? ( + st()) : ( + st());

  

  

  //副菜(装饰者) 具体装饰类 甜点 用来动态装饰主菜 牛排或猪排

  class Dessert : CondimentDecorator          //类似前一例的 ConcreteDecoratorD

  

  public Dessert(Meal meal)       //构造函数

  

  Description = 甜点 ;

  al = meal;

  

  public override int cost()         //具体装饰对象 D 的操作

  

  return + st();

  

  

   // end of namespace

  图  示例 _Steak aspx cs 的执行结果 扩展功能时 主菜和副菜可任意混合搭配使用

  原理与本帖之前提过的相同 将来店里若要推出 羊排 鱼排 …等新的主菜 (被装饰者) 时 只要增加一个类 去继承 Meal 这个抽象的基类即可 若要推出新的副菜 (装饰者) 也一样 只要增加一个类 去继承 CondimentDecorator 抽象类即可 我们透过这个重要的 CondimentDecorator 抽象类 实现 UML 的 Aggregation (聚合) 观念 以「装饰模式」取代传统「继承」的做法 也同时降低了「主菜」 「副菜」这两种类之间的耦合度

  这个 CondimentDecorator 抽象类的作用 还可将未来所有种类的副菜 一些「共同的」行为或职责写在里面 而且 它还可用来区分哪些类是主菜 哪些类是副菜 因为主菜 副菜都是继承自 Meal 这个最顶层的基类 若没有这个 CondimentDecorator 抽象类 将来 Class Diagram 变得很复杂时 会不易分辨某个类是主菜或副菜

  此外 也因为主菜 (被装饰者) 副菜 (装饰者) 都是继承自 Meal 这个最顶层的基类 所以客户端程序 (Page_Load) 在引用他们的对象时 把 new 出来的主菜实例 new 出来的副菜实例 再抛入其他副菜实例的构造函数中 也会相容 不会发生错误 在相互引用和「运行」时期的「动态」扩展上 变得很有弹性

  最后补充一点 Decorator 模式中 装饰者类对象 在组合时的「顺序」亦很重要 本帖图 中 ConcreteDecoratorA ConcreteDecoratorB ConcreteDecoratorC 等具体装饰者 在动态添加和组合时 在某些实际应用中必须依照一定的顺序 若顺序相反可能会导致程序执行结果不合理 但本帖所提供的几个例子中并不需要考虑到这点

  

  Decorator Pattern 适用的情景

  你拥有一个已存在的组件类 却无法继承它 (subclassing)

  能够动态地为对象添加职责 (添加状态和行为)

  改变类中的成员和行为 但不影响其他对象

  希望能便于职责的撤消

  不想用「继承」来扩展行为 其中一种原因是避免当一些功能要交叉搭配引用时 单独用「继承」来设计会产生太多的子类 太复杂的类图结构 [ ] [ ] 另一种考量可能是因为类的定义被隐藏 或类的定义不能用于生成子类

  Decorator Pattern 的优点

  可避免单独使用「继承」时 在扩展时不够弹性 且可能衍生过多的子类

  扩展时不需要修改既有的代码

  可在执行时期 动态地添加新行为 (职责)

  Decorator Pattern 的缺点

  可能会在程序中出现许多的小型类 亦即需要编写很多 ConcreteDecorator 类 (具体装饰者)

  若过度使用 Decorator 模式 会让程序逻辑变得很复杂

  别人较不易理解设计方式及代码 排查故障 追踪和调试也比较困难

  Decorator Pattern 的其他特性

  每个要装饰的功能 都放在单独的类中

  我们可以用无数个装饰者 去包装一个组件

  「装饰者」可以在「被装饰者」前面或后面 添加自己的行为 甚至将「被装饰者」的行为整个取代掉 以达到特定的目的

  「被装饰者」并不需要知道它已经被「装饰」过了 亦即 Component 类 (对象) 并不需要知道 Decorator 类 (对象) 的存在 且 Decorator 也仅仅认识 Component

  

  最后再补充 在 Java I/O NET I/O NET System Windows Controls 命名空间中 很多地方都有用到 Decorator 模式来设计 [ ] [ ] [ ] 例如我们查询 MSDN Library 会发现 NET x 版本的 Windows Forms 其 API 里 甚至还有增加一个新的 Decorator 类 如下

  System Object

  System Windows Threading DispatcherObject

  System Windows DependencyObject

  System Windows Media Visual

  System Windows UIElement

  System Windows FrameworkElement

  System Windows Controls Decorator

  System Windows Controls Viewbox

  System Windows Controls Decorator 类

  提供在单个子元素(如 Border 或 Viewbox)上或周围应用效果的元素的基类

   cn/library/ntrols decorator aspx

   cn/library/ntrols decorator(VS ) aspx

  以下列出 Decorator Pattern 用于实际程序的四种方式 [ ]

  装饰模式非常适合于「图形程序」 亦适合于「视频和声音」 比如 视频流可用不同的比率进行压缩 而声音可以输入给同声传译服务

  装饰模式如此有用 以致于 NET 中 已经有实际的 Decorator 类了 如刚才提到的 在 System Windows Controls 命名空间中 提供了一些基类 可用于给其他的图形元素添加特殊效果 如 Border 类和 Viewbox 类

  在如今的移动设备上 Web 浏览器及其他应用中 也大量运用 Decorator Pattern 比如这些设备上可以创建适合小屏幕显示的对象 跟标准桌面机上的浏览器相比 这些屏幕可能也含有滚动条 但不含有标题栏 [ ]

  从更一般的层次上 NET 的 I/O API 中 到处都是装饰模式 [ ] 如下方的体系

  System Object

  System MarshalByRefObject

  System IO Stream

  System IO BufferedStream

  System IO FileStream

  System IO MemoryStream

  System Net Sockets NeorkStream

  System Security Cryptography CryptoStream

  上方体系的最后五个子类 有的就装饰了 Steram 这个父类 (如 BufferedStream 类) 因为它们从它继承 同时又含有 Stream 类的实例 而且这个实例是子类被构造时传递进去的 (如同本帖第二个西餐店的示例中 副菜的构造函数的写法) 如下方 MSDN Library 代码的 BufferedStream 类 其构造函数的第一个参数 即为其父类 System IO Stream 的变量 stream

  BufferedStream 构造函数

  public BufferedStream(

  Stream stream

  )

  我们再举个生活中的例子 俗话说 人在衣着马在鞍 把这用「装饰模式」的语境翻译一下 人通过漂亮的衣服装饰后 男人变帅了 女人变漂亮了 对应上面的类图 这里「人」对应于 ConcreteComponent 而「漂亮衣服」则对应于 ConcreteDecorator 换个角度来说 人和漂亮衣服组合在一起【HAS A】 有了帅哥或美女 但是他们还是人【IS A】 还要做人该做的事情 但是可能会对异性更有吸引力了(扩展功能)!

  

  上方 Justin 前辈 其文章的「煮咖啡」示例 [ ] 是引用自 O Reilly 出版社的「Head First 设计模式」这本书的第三章 [ ] 该文的煮咖啡类图中 HouseBlend (家常咖啡) DarkRoast (深度烘培咖啡) Espresso (意大利特浓咖啡) Decaf (无咖啡因咖啡) 这四种咖啡 (饮料) 即为「被装饰者」 等同本帖上图 中的 ConcreteComponent 类 该文类图中的 CondimentDecorator 抽象类 等同本帖上图 中最重要的 Decorator 抽象类 亦即「装饰者」的抽象定义 该文类图中的 Milk Mocha Soy Whip 这四种调料 (调味品) 即为具体的「装饰者」 亦即在本帖一开始提到 这四种调料 可弹性地 (flexible) 混合著搭配使用 而且是在「执行」时期「动态」地进行扩展 亦即动态地装饰 HouseBlend DarkRoas Espresso Decaf 这四种咖啡

  接下来 我们用另一个简单的例子来实现 Decorator 模式 并改用 ASP NET 网页程序来实现 类图及代码如下方图 所示 执行结果如下图 所示

  此为一个西餐牛排馆的计费程序 这间牛排馆有两种主菜 牛排和猪排 此为「被装饰者」 有四种副菜 面条 生菜沙拉 饮料 (热饮或冷饮) 甜点 此为「装饰者」 客人点餐时 可点一种主菜 副菜可点一份 可点多份 也可不点 (有弹性地将多个「装饰者」混合著搭配) 每样主菜和副菜都有各自的价格 全部相加后 即为一或多位客人的用餐费用 而且我们希望 将来这个计费程序写好后 未来即使要修改价格 或添加新的主菜和副菜时 都不用再修改既有的程序

  

  图  示例 _Steak aspx cs 的 Class Diagram

   _Steak aspx cs

  using System;

  using blogs WizardWu sample ;       //客户端程序调用服务器端的组件和类

  //客户端程序

  public partial class _ _Steak : System Web UI Page

  

  protected void Page_Load(object sender EventArgs e)

  

  //点一客猪排 不需要副菜 并列出它的描述与价格

  Meal meal = new PorkChop();

  //Meal meal = new BeefSteak();

  Response Write(meal Description + = $ + st() +

   );

  //点一客猪排 副菜只要面条 并列出它的描述与价格

  Meal meal = new PorkChop();

  meal = new Noodle(meal );          //用 Noodle 装饰它 (运行时动态地增加职责)

  Response Write(meal Description + = $ + st() +

   );

  //点一客猪排 因为这个人食量大 副菜要两份面条 一杯饮料(热饮) 一盘甜点 并列出它的描述与价格

  Meal meal = new PorkChop();

  meal = new Noodle(meal );          //用 Noodle 装饰它

  meal = new Noodle(meal );          //用第二个 Noodle 装饰它

  meal = new Drink(meal true);    //用 Drink 装饰它

  meal = new Dessert(meal );         //用 Dessert 装饰它

  Response Write(meal Description + = $ + st() +

   );

  //第四个人不吃猪肉 因此主菜改点一客牛排 副菜要一盘沙拉 一杯饮料(冷饮) 并列出它的描述与价格

  Meal meal = new BeefSteak();

  meal = new Salad(meal );            //用 Salad 装饰它 (运行时动态地增加职责)

  meal = new Drink(meal false);   //用 Drink 装饰它

  Response Write(meal Description + = $ + st() +

   );

  

  

  //服务器端程序

  namespace blogs WizardWu sample

  

  //装饰者(副菜) 被装饰者(主菜)的共同基类 类似前一例的 Component 抽象类

  abstract class Meal

  

  protected string description = 西餐 ;

  public virtual string Description

  

  get return description;

  set description = value;

  

  abstract public int cost();        //必须在子类实作的抽象方法 除非该个子类也是抽象类

  

  //主菜(被装饰者) 类似前一例的 ConcreteComponent 类

  class PorkChop : Meal                //主菜 猪排 以后执行时 才会动态被副菜装饰

  

  public PorkChop()                  //构造函数

  

  Description = 猪排 ;

  

  public override int cost()         //具体对象的操作

  

  return ;                        //猪排的价格 现在不需要管「副菜」的价格 直接返回猪排的价格即可

  

  

  //主菜(被装饰者) 类似前一例的 ConcreteComponent 类

  class BeefSteak : Meal               //主菜 牛排 以后执行时 才会动态被副菜装饰

  

  public BeefSteak()                 //构造函数

  

  Description = 牛排 ;

  

  public override int cost()         //具体对象的操作

  

  return ;                        //牛排的价格 现在不需要管「副菜」的价格 直接返回牛排的价格即可

  

  

  //装饰者 类似前一例的 Decorator 抽象类 此为所有「副菜(装饰者)」的父类

  //功能一 未来所有可能添加的副菜 其共同的属性 行为 都可放在这个类里

  //功能二 用来「分类」哪些是主菜 哪些是副菜

  abstract class CondimentDecorator : Meal      //副菜 用来「动态」装饰主菜和 Meal 基类 亦即在运行时动态地替他们增加职责

  

  //以父类声明的字段

  //实现 UML Class Diagram 的 Aggregation 指向 Meal 对象的指针

  protected Meal meal;

  public override string Description

  

  get return meal Description + + + description;

  set description = value;

  

  

  //副菜(装饰者) 具体装饰类 面条 用来动态装饰主菜 牛排或猪排

  class Noodle : CondimentDecorator            //类似前一例的 ConcreteDecoratorA

  

  public Noodle(Meal meal)        //构造函数

  

  Description = 面条 ;

  al = meal;

  

  public override int cost()         //具体装饰对象 A 的操作

  

  return + st();

  

  

  //副菜(装饰者) 具体装饰类 沙拉 用来动态装饰主菜 牛排或猪排

  class Salad : CondimentDecorator             //类似前一例的 ConcreteDecoratorB

  

  public Salad(Meal meal)          //构造函数

  

  Description = 生菜沙拉 ;

  al = meal;

  

  public override int cost()         //具体装饰对象 B 的操作

  

  return + st();

  

  

  //副菜(装饰者) 具体装饰类 饮料 用来动态装饰主菜 牛排或猪排

  class Drink : CondimentDecorator             //类似前一例的 ConcreteDecoratorC

  

  private bool isHot;

  public bool IsHot

  

  get return isHot;

  

  public Drink(Meal meal bool isHot)          //构造函数

  

  Description = (isHot) ? 热饮 : 冷饮 ;

  al = meal;

  this isHot = isHot;

  

  public override int cost()         //具体装饰对象 C 的操作 冷饮比热饮多收 块钱

  

  return (isHot) ? ( + st()) : ( + st());

  

  

  //副菜(装饰者) 具体装饰类 甜点 用来动态装饰主菜 牛排或猪排

  class Dessert : CondimentDecorator          //类似前一例的 ConcreteDecoratorD

  

  public Dessert(Meal meal)       //构造函数

  

  Description = 甜点 ;

  al = meal;

  

  public override int cost()         //具体装饰对象 D 的操作

  

  return + st();

  

  

   // end of namespace

  图  示例 _Steak aspx cs 的执行结果 扩展功能时 主菜和副菜可任意混合搭配使用

  原理与本帖之前提过的相同 将来店里若要推出 羊排 鱼排 …等新的主菜 (被装饰者) 时 只要增加一个类 去继承 Meal 这个抽象的基类即可 若要推出新的副菜 (装饰者) 也一样 只要增加一个类 去继承 CondimentDecorator 抽象类即可 我们透过这个重要的 CondimentDecorator 抽象类 实现 UML 的 Aggregation (聚合) 观念 以「装饰模式」取代传统「继承」的做法 也同时降低了「主菜」 「副菜」这两种类之间的耦合度

  这个 CondimentDecorator 抽象类的作用 还可将未来所有种类的副菜 一些「共同的」行为或职责写在里面 而且 它还可用来区分哪些类是主菜 哪些类是副菜 因为主菜 副菜都是继承自 Meal 这个最顶层的基类 若没有这个 CondimentDecorator 抽象类 将来 Class Diagram 变得很复杂时 会不易分辨某个类是主菜或副菜

  此外 也因为主菜 (被装饰者) 副菜 (装饰者) 都是继承自 Meal 这个最顶层的基类 所以客户端程序 (Page_Load) 在引用他们的对象时 把 new 出来的主菜实例 new 出来的副菜实例 再抛入其他副菜实例的构造函数中 也会相容 不会发生错误 在相互引用和「运行」时期的「动态」扩展上 变得很有弹性

  最后补充一点 Decorator 模式中 装饰者类对象 在组合时的「顺序」亦很重要 本帖图 中 ConcreteDecoratorA ConcreteDecoratorB ConcreteDecoratorC 等具体装饰者 在动态添加和组合时 在某些实际应用中必须依照一定的顺序 若顺序相反可能会导致程序执行结果不合理 但本帖所提供的几个例子中并不需要考虑到这点

  

  Decorator Pattern 适用的情景

  你拥有一个已存在的组件类 却无法继承它 (subclassing)

  能够动态地为对象添加职责 (添加状态和行为)

  改变类中的成员和行为 但不影响其他对象

  希望能便于职责的撤消

  不想用「继承」来扩展行为 其中一种原因是避免当一些功能要交叉搭配引用时 单独用「继承」来设计会产生太多的子类 太复杂的类图结构 [ ] [ ] 另一种考量可能是因为类的定义被隐藏 或类的定义不能用于生成子类

  Decorator Pattern 的优点

  可避免单独使用「继承」时 在扩展时不够弹性 且可能衍生过多的子类

  扩展时不需要修改既有的代码

  可在执行时期 动态地添加新行为 (职责)

  Decorator Pattern 的缺点

  可能会在程序中出现许多的小型类 亦即需要编写很多 ConcreteDecorator 类 (具体装饰者)

  若过度使用 Decorator 模式 会让程序逻辑变得很复杂

  别人较不易理解设计方式及代码 排查故障 追踪和调试也比较困难

  Decorator Pattern 的其他特性

  每个要装饰的功能 都放在单独的类中

  我们可以用无数个装饰者 去包装一个组件

  「装饰者」可以在「被装饰者」前面或后面 添加自己的行为 甚至将「被装饰者」的行为整个取代掉 以达到特定的目的

  「被装饰者」并不需要知道它已经被「装饰」过了 亦即 Component 类 (对象) 并不需要知道 Decorator 类 (对象) 的存在 且 Decorator 也仅仅认识 Component

  

  最后再补充 在 Java I/O NET I/O NET System Windows Controls 命名空间中 很多地方都有用到 Decorator 模式来设计 [ ] [ ] [ ] 例如我们查询 MSDN Library 会发现 NET x 版本的 Windows Forms 其 API 里 甚至还有增加一个新的 Decorator 类 如下

  System Object

  System Windows Threading DispatcherObject

  System Windows DependencyObject

  System Windows Media Visual

  System Windows UIElement

  System Windows FrameworkElement

  System Windows Controls Decorator

  System Windows Controls Viewbox

  System Windows Controls Decorator 类

  提供在单个子元素(如 Border 或 Viewbox)上或周围应用效果的元素的基类

   cn/library/ntrols decorator aspx

   cn/library/ntrols decorator(VS ) aspx

  以下列出 Decorator Pattern 用于实际程序的四种方式 [ ]

  装饰模式非常适合于「图形程序」 亦适合于「视频和声音」 比如 视频流可用不同的比率进行压缩 而声音可以输入给同声传译服务

  装饰模式如此有用 以致于 NET 中 已经有实际的 Decorator 类了 如刚才提到的 在 System Windows Controls 命名空间中 提供了一些基类 可用于给其他的图形元素添加特殊效果 如 Border 类和 Viewbox 类

  在如今的移动设备上 Web 浏览器及其他应用中 也大量运用 Decorator Pattern 比如这些设备上可以创建适合小屏幕显示的对象 跟标准桌面机上的浏览器相比 这些屏幕可能也含有滚动条 但不含有标题栏 [ ]

  从更一般的层次上 NET 的 I/O API 中 到处都是装饰模式 [ ] 如下方的体系

  System Object

  System MarshalByRefObject

  System IO Stream

  System IO BufferedStream

  System IO FileStream

  System IO MemoryStream

  System Net Sockets NeorkStream

  System Security Cryptography CryptoStream

  上方体系的最后五个子类 有的就装饰了 Steram 这个父类 (如 BufferedStream 类) 因为它们从它继承 同时又含有 Stream 类的实例 而且这个实例是子类被构造时传递进去的 (如同本帖第二个西餐店的示例中 副菜的构造函数的写法) 如下方 MSDN Library 代码的 BufferedStream 类 其构造函数的第一个参数 即为其父类 System IO Stream 的变量 stream

  BufferedStream 构造函数

  public BufferedStream(

cha138/Article/program/net/201311/12524

相关参考

知识大全 C# Design Patterns (4) - Proxy

C#DesignPatterns(4)-Proxy  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 

炭循环微电解反应器处理腈纶废水的Design-Expert设计优化

[摘要]利用Design-Expert7.1试验设计系统对炭循环微电解反应器处理腈纶废水的试验进行了试验设计和试验结果分析;通过试验设计分析得出了腈纶废水COD去除率与试验影响因素之间的定量关系模型,

炭循环微电解反应器处理腈纶废水的Design-Expert设计优化

[摘要]利用Design-Expert7.1试验设计系统对炭循环微电解反应器处理腈纶废水的试验进行了试验设计和试验结果分析;通过试验设计分析得出了腈纶废水COD去除率与试验影响因素之间的定量关系模型,

炭循环微电解反应器处理腈纶废水的Design-Expert设计优化

[摘要]利用Design-Expert7.1试验设计系统对炭循环微电解反应器处理腈纶废水的试验进行了试验设计和试验结果分析;通过试验设计分析得出了腈纶废水COD去除率与试验影响因素之间的定量关系模型,

知识大全 C#图片处理的3种高级实用方法

C#图片处理的3种高级实用方法  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  本文介绍C#图片处

知识大全 C#使用BerkeleyDB操作简介[3]

C#使用BerkeleyDB操作简介[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!while

知识大全 C# 3.0中的分部方法

C#3.0中的分部方法  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  分部方法的语法  在看C#

知识大全 认识C#中使用Nullable类型[3]

认识C#中使用Nullable类型[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在其它关

知识大全 C#正则表达式整理备忘(3)

C#正则表达式整理备忘(3)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  ()回溯与非回溯  

知识大全 一个关于C#的睡前故事[3]

NET委托:一个关于C#的睡前故事[3]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!