zoukankan      html  css  js  c++  java
  • 装饰者模式 Decorator

    项目:咖啡计费系统

    背景:现有系统中有一个抽象类Beverage,有2个抽象方法GetDescription和Cost。

     1 namespace DecoratorPattern
     2 {
     3     /// <summary>
     4     /// 饮料抽象类
     5     /// </summary>
     6     public abstract  class Beverage
     7     {
     8         protected string description = "饮料";
     9         protected float price = 0f;
    10         public abstract string GetDescription();
    11         
    12         public abstract float Cost();
    13     }
    14 }

    需求:目前有综合咖啡、深焙咖啡、浓缩咖啡,调料有牛奶、摩卡、豆浆、奶泡。未来可能增加新的咖啡种类和调料,当顾客点咖啡时,要求能够获得咖啡的描述和价格。

    设计方案1:设计综合咖啡、深焙咖啡、浓缩咖啡4个子类,继承Beverage。再用这4个子类分别派生4个子类,带有牛奶的综合咖啡,带有摩卡的综合咖啡,带有豆浆的综合咖啡...

    分析:缺点时显而易见的,这样做导致“类爆炸”,一共需要3*4=12个子类。

    设计方案2:把调料作为咖啡的属性设置在Beverage里,并增加方法HasMilk(),SetMilk()等类似的方法。

    分析:缺点这样做无疑时从一个灾难跳进另一个灾难中。我们在开发中应当尽量避免修改已有代码,遵循“开闭原则”。而且当增加新的饮料时,又要修改基类。

    另一个灾难是,子类在计算价格时,需要大量的分支结构来判断是否包含某种调料,以计算咖啡的价格,我们总是尽量的避免复杂的分支结构,这使得维护变得非常困难。

    还有针对实现编程带来的问题,不能够动态的添加职责。

    装饰者模式 Decorator 闪亮登场:

    装饰者模式动态的将职责附加到对象上。若要扩展共呢个,装饰者提供了比继承更有弹性的替代方案。

    1. 4个基类继承Beverage

     1 namespace DecoratorPattern
     2 {
     3     /// <summary>
     4     /// 综合咖啡
     5     /// </summary>
     6     public class HouseBlend:Beverage
     7     {
     8         public HouseBlend(float price)
     9         {
    10             this.price = price;
    11             this.description = "综合咖啡";
    12         }
    13 
    14         public override float Cost()
    15         {
    16             return price;
    17         }
    18         public override string GetDescription()
    19         {
    20             return this.description;
    21         }
    22     }
    23 }
     1 namespace DecoratorPattern
     2 {
     3     /// <summary>
     4     /// 深焙咖啡
     5     /// </summary>
     6     public class DarkRoast:Beverage
     7     {
     8         public DarkRoast(float price)
     9         {
    10             this.price = price;
    11             this.description = "深焙咖啡";
    12         }
    13         public override string GetDescription()
    14         {
    15             return this.description;
    16         }
    17         public override float Cost()
    18         {
    19             return price;
    20         }
    21     }
    22 }
     1 namespace DecoratorPattern
     2 {
     3     /// <summary>
     4     /// 浓缩咖啡
     5     /// </summary>
     6     public class Espresso:Beverage
     7     {
     8         public Espresso(float price)
     9         {
    10             this.price = price;
    11             this.description = "浓缩咖啡";
    12         }
    13         public override float Cost()
    14         {
    15             return this.price;
    16         }
    17         public override string GetDescription()
    18         {
    19             return this.description;
    20         }
    21     }
    22 }

    装饰者继承Beverage,注意这里继承的目的并不是为了获得基类的功能,而是为了类型匹配,达到多态的目的,获得功能由组合来实现。因此每一个装饰者都需要维护一个Beverage引用。

     1 namespace DecoratorPattern
     2 {
     3     /// <summary>
     4     /// 摩卡装饰者,为了能够取代Beverage,所以CondimentDecorator继承自Beverage,目的并非获得Beverage
     5     /// 而是为了类型匹配
     6     /// </summary>
     7     public class Mocha : Beverage
     8     {
     9         //持有抽象类饮料的引用,达到运行时添加职责的目的
    10         Beverage beverage;
    11         //包装Beverage
    12         public Mocha(Beverage b, float price)
    13         {
    14             beverage = b;
    15             this.price = price;
    16         }
    17         public override float Cost()
    18         {
    19             return beverage.Cost() + price;
    20         }
    21 
    22         public override string GetDescription()
    23         {
    24             return beverage.GetDescription() + ", 摩卡";
    25         }
    26     }
    27 }
     1 namespace DecoratorPattern
     2 {
     3     /// <summary>
     4     /// 奶泡装饰者
     5     /// </summary>
     6     public class Whip : Beverage
     7     {
     8         private Beverage beverage;
     9         public Whip(Beverage b, float price)
    10         {
    11             beverage = b;
    12             this.price = price;
    13         }
    14         public override float Cost()
    15         {
    16             return beverage.Cost() + price;
    17         }
    18         public override string GetDescription()
    19         {
    20             return (beverage.GetDescription() + " ,奶泡");
    21         }
    22     }
    23 }
     1 namespace DecoratorPattern
     2 {
     3     /// <summary>
     4     /// 豆浆装饰者
     5     /// </summary>
     6     public class Soy : Beverage
     7     {
     8         private Beverage beverage;
     9         public Soy(Beverage b, float price)
    10         {
    11             this.price = price;
    12             beverage = b;
    13         }
    14         public override float Cost()
    15         {
    16             return beverage.Cost() + price;
    17         }
    18 
    19         public override string GetDescription()
    20         {
    21             return( beverage.GetDescription() + ", 豆浆");
    22         }
    23     }
    24 }

    客户端类:CoffeeShop.cs

     1 using System;
     2 
     3 namespace DecoratorPattern
     4 {
     5     class CoffeeShop
     6     {
     7         static void Main(string[] args)
     8         {
     9             //来一杯浓缩咖啡,不要调料
    10             Beverage beverage = new Espresso(1.99f);
    11             Console.WriteLine(beverage.GetDescription() + "$" + beverage.Cost());
    12 
    13             //来一杯摩卡奶泡深焙咖啡
    14             Beverage beverage2 = new DarkRoast(0.99f);
    15             beverage2 = new Whip(beverage2, 0.1f);       //用奶泡装饰深焙咖啡
    16             beverage2 = new Mocha(beverage2, 0.2f);      //再用摩卡装饰
    17             Console.WriteLine(beverage2.GetDescription() + "$" + beverage2.Cost());
    18 
    19 
    20             Console.ReadKey();
    21         }
    22 
    23     }
    24 }

    运行结果:

    参考资料《Head First 设计模式》

  • 相关阅读:
    什么是ORM
    ORM优缺点
    Azure 中快速搭建 FTPS 服务
    连接到 Azure 上的 SQL Server 虚拟机(经典部署)
    在 Azure 虚拟机中配置 Always On 可用性组(经典)
    SQL Server 2014 虚拟机的自动备份 (Resource Manager)
    Azure 虚拟机上的 SQL Server 常见问题
    排查在 Azure 中新建 Windows 虚拟机时遇到的经典部署问题
    上传通用化 VHD 并使用它在 Azure 中创建新 VM
    排查在 Azure 中新建 Windows VM 时遇到的部署问题
  • 原文地址:https://www.cnblogs.com/blackteeth/p/10212218.html
Copyright © 2011-2022 走看看