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 设计模式》

  • 相关阅读:
    应用量化时代 | 微服务架构的服务治理之路
    API网关——Kong实践分享
    容器云未来:Kubernetes、Istio 和 Knative
    微服务网关实战——Spring Cloud Gateway
    服务迁移之路 | Spring Cloud向Service Mesh转变
    基于事件驱动机制,在Service Mesh中进行消息传递的探讨
    MSMQ 和 MQTT
    MQTT和WebSocket
    NetCore WebSocket 即时通讯示例
    .NET 即时通信,WebSocket服务端实例
  • 原文地址:https://www.cnblogs.com/blackteeth/p/10212218.html
Copyright © 2011-2022 走看看