项目:咖啡计费系统

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

 namespace DecoratorPattern
{
/// <summary>
/// 饮料抽象类
/// </summary>
public abstract class Beverage
{
protected string description = "饮料";
protected float price = 0f;
public abstract string GetDescription(); public abstract float Cost();
}
}

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

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

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

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

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

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

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

装饰者模式 Decorator 闪亮登场:

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

1. 4个基类继承Beverage

 namespace DecoratorPattern
{
/// <summary>
/// 综合咖啡
/// </summary>
public class HouseBlend:Beverage
{
public HouseBlend(float price)
{
this.price = price;
this.description = "综合咖啡";
} public override float Cost()
{
return price;
}
public override string GetDescription()
{
return this.description;
}
}
}
 namespace DecoratorPattern
{
/// <summary>
/// 深焙咖啡
/// </summary>
public class DarkRoast:Beverage
{
public DarkRoast(float price)
{
this.price = price;
this.description = "深焙咖啡";
}
public override string GetDescription()
{
return this.description;
}
public override float Cost()
{
return price;
}
}
}
 namespace DecoratorPattern
{
/// <summary>
/// 浓缩咖啡
/// </summary>
public class Espresso:Beverage
{
public Espresso(float price)
{
this.price = price;
this.description = "浓缩咖啡";
}
public override float Cost()
{
return this.price;
}
public override string GetDescription()
{
return this.description;
}
}
}

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

 namespace DecoratorPattern
{
/// <summary>
/// 摩卡装饰者,为了能够取代Beverage,所以CondimentDecorator继承自Beverage,目的并非获得Beverage
/// 而是为了类型匹配
/// </summary>
public class Mocha : Beverage
{
//持有抽象类饮料的引用,达到运行时添加职责的目的
Beverage beverage;
//包装Beverage
public Mocha(Beverage b, float price)
{
beverage = b;
this.price = price;
}
public override float Cost()
{
return beverage.Cost() + price;
} public override string GetDescription()
{
return beverage.GetDescription() + ", 摩卡";
}
}
}
 namespace DecoratorPattern
{
/// <summary>
/// 奶泡装饰者
/// </summary>
public class Whip : Beverage
{
private Beverage beverage;
public Whip(Beverage b, float price)
{
beverage = b;
this.price = price;
}
public override float Cost()
{
return beverage.Cost() + price;
}
public override string GetDescription()
{
return (beverage.GetDescription() + " ,奶泡");
}
}
}
 namespace DecoratorPattern
{
/// <summary>
/// 豆浆装饰者
/// </summary>
public class Soy : Beverage
{
private Beverage beverage;
public Soy(Beverage b, float price)
{
this.price = price;
beverage = b;
}
public override float Cost()
{
return beverage.Cost() + price;
} public override string GetDescription()
{
return( beverage.GetDescription() + ", 豆浆");
}
}
}

客户端类:CoffeeShop.cs

 using System;

 namespace DecoratorPattern
{
class CoffeeShop
{
static void Main(string[] args)
{
//来一杯浓缩咖啡,不要调料
Beverage beverage = new Espresso(1.99f);
Console.WriteLine(beverage.GetDescription() + "$" + beverage.Cost()); //来一杯摩卡奶泡深焙咖啡
Beverage beverage2 = new DarkRoast(0.99f);
beverage2 = new Whip(beverage2, 0.1f); //用奶泡装饰深焙咖啡
beverage2 = new Mocha(beverage2, 0.2f); //再用摩卡装饰
Console.WriteLine(beverage2.GetDescription() + "$" + beverage2.Cost()); Console.ReadKey();
} }
}

运行结果:

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

装饰者模式 Decorator的更多相关文章

  1. 浅谈设计模式--装饰者模式(Decorator Pattern)

    挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...

  2. 【PHP设计模式 09_ZhuangShiQi.php】装饰器模式 (decorator)

    <?php /** * [装饰器模式 (decorator)] * 有时候发布一篇文章需要经过很多人手,层层处理 */ header("Content-type: text/html; ...

  3. 设计模式(八)装饰器模式Decorator(结构型)

    设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法 ...

  4. 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法

    装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...

  5. 设计模式 - 装饰者模式(Decorator Pattern) 具体解释

    装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...

  6. 装饰器模式-Decorator(Java实现)

    装饰器模式-Decorator(Java实现) 装饰器模式允许向一个现有的对象添加新的功能, 同时又不改变其结构. 其中 "现有对象"在本文中是StringDisplay类. 添加 ...

  7. 设计模式学习--装饰者模式(Decorator Pattern)

    概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...

  8. 大话设计模式--装饰者模式 Decorator -- C++实现实例

    1.装饰者模式 Decorator 动态地给一个对象添加一个额外的职责, 就添加功能来说, 装饰模式比生成子类更为灵活. 每个装饰对象的实现和如何使用这个对象分离,  每个装饰对象只关心自己的功能,不 ...

  9. 设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)

    在前两篇博客中详细的介绍了"策略模式"和“观察者模式”,今天我们就通过花瓶与鲜花的例子来类比一下“装饰模式”(Decorator Pattern).在“装饰模式”中很好的提现了开放 ...

随机推荐

  1. port bridge enable命令导致的环路

    1.故障描述 前几天机房一台连接数据中心与核心交换的交换机宕机(硬件故障),机房有备用的设备,随即更换(配置也是早就配置好了的),但是下午就出现数据中心网络丢包问题,表现为存在mac漂移 2.拓扑 核 ...

  2. (72)Wangdao.com第十二天_JavaScript 错误处理机制

    1. Error 实例对象 JavaScript 解析或运行时,一旦发生错误,引擎就会抛出一个错误对象. JavaScript 原生提供Error构造函数,所有抛出的错误都是这个构造函数的实例. va ...

  3. Go语言基础之time包

    Go语言基础之time包 时间和日期是我们编程中经常会用到的,本文主要介绍了Go语言内置的time包的基本用法. Go语言中导入包 Go语言中使用import关键字导入包,包的名字使用双引号(”)包裹 ...

  4. [LeetCode] Jewels and Stones 珠宝和石头

    You're given strings J representing the types of stones that are jewels, and S representing the ston ...

  5. Educational Codeforces Round 6

    620A - Professor GukiZ's Robot    20171122 \(ans=max(\left | x2-x1 \right |,\left | y2-y1 \right |)\ ...

  6. 秒杀linux下系统调用fork()面试题(转)

    https://blog.csdn.net/chdhust/article/details/10579001 https://www.cnblogs.com/clover-toeic/p/375443 ...

  7. 扫毒>>观后感

    观后感 中学时期,甚至在小学时期,那个很懵懂的年龄, 看了什么电影,去了哪里,都要写观后感. 那个时候觉得观后感很玄学,为啥看的电影都一样,去的地方都一样, 为啥人家的观后感貌似说的真的有那么点道理, ...

  8. lua 操作redis

    Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行.使用脚本的好处如下: 1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在red ...

  9. 腾讯云CDB回档失败浅析

    Ⅰ.先看问题 先简单介绍下cdb的回档功能,回档分为极速.快速.普通,分别对应指定表.指定库.整个实例回档. 控制台报错回档任务执行失败 提示信息:rollback table failed:SQL ...

  10. ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key

    约束字段为自动增长,被约束的字段必须同时被key约束 没有设置成primary key 时,会报错. 加上primary key 则设置成功.