Head First设计模式——装饰者模式
前言:对于设计模式我们有时候在想是否有必要,因为实际开发中我们没有那么多闲工夫去套用这么多设计模式,也没有必要为了模式而模式。
通常这些模式会引入新的抽象层,增加代码的复杂度,但是当我们掌握了这些设计模式,
在系统中比较棘手或者需要以后修改扩展的地方采用了合适的设计模式会让我们的系统易于扩展维护甚至工作变得轻松很多。
对于这一点我深有体会,有时候设计的比较好的功能模块在后来客户改变需求的时候变得很容易且方便添加修改。
但是如果比较糟糕偷懒的方式会让我们对自己的代码修改变得害怕,害怕客户提需求,害怕去动自己的代码。
所以对于框架或者这些设计模式我们可以不用过度使用,但是要用的时候能有储备或者脑袋里面会闪现出对应的解决方案。
这样不光会提升我们的编码兴趣对我们开发的产品也更有信心。
本篇就聚焦Head First设计模式中的装饰者模式进行学习和总结。
咖啡店结算案例
咖啡店卖各种咖啡,根据调料不一样就会有不同价格的咖啡饮品可选,那么对应的咖啡饮品价格也不一样。
咖啡是需要按照基础价格+调料组合计算价格的,那按照继承的方式我们抽象一个饮料基类Beverage,Beverage拥有Cost抽象方法用于计算价格。
其他咖啡饮品继承Beverage实现Cost方法根据自己的调料计算价格。
public abstract class Beverage {
public abstract void Description();
public abstract float Cost();
}
public class DarkRoast : Beverage
{
public override void Description()
{
//深焙咖啡
}
public override float Cost()
{
//各种调料价格计算
}
}
对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作。
如果说某一种调料的价格变化我们还得去每个子类里面改变价格。
那我们针对变化的调料部分是不是让他们按照实际需求组合,在子类中确定自己加哪些调料就行了,似乎这种方式会减少重写和维护难度。
按照这个思路将Beverage改造,将是否有调料定义成变量,然后Cost方法不再抽象而是提供实现。
public abstract class Beverage
{
//牛奶
public bool Milk { get; set; }
//糖
public bool Suger { get; set; }
//摩卡
public bool Mocha { get; set; }
public abstract void Description();
public virtual float Cost()
{
float price = 0;
if (Milk)
{
price += 1;
}
if (Suger)
{
price += 2;
}
if (Mocha)
{
price += 3;
}
return price;
}
} public class DarkRoast : Beverage
{
public override void Description()
{
Console.WriteLine("深焙咖啡");
} public override float Cost()
{
Milk = true;
Suger = true;
return 1.1f+base.Cost();
}
}

这种方式比之前的继承的确好了许多,不过还是有如下几个问题:
1、调料价格改变会使我们改变现有代码。
2、需要添加新的调料,我们就需要添加新的方法并改变Beverage基类的Cost方法。
3、某些调料可能并不适合其他饮品,例如茶还要继承这些不属于它的调料。
4、如果顾客需要双份糖,Cost方法就会失效。
接下来我们就用"装饰者模式"来更好的设计该案例。
认识装饰者模式
这里有一个设计模式很重要的设计原则:类应该对扩展开放,对修改关闭(开闭原则)
装饰模式同样遵循开闭原则。
对于咖啡店来说,主体是饮料不变,变化的是调料。我们以饮料为主体,其他调料来“装饰”饮料。比如顾客想要加了Mocha(摩卡)的 DarkRoast(深焙咖啡),我们要做的是:
1、制造一个DarkRoast对象
2、以Mocha(摩卡)对象装饰它
3、调用Cost()方法,并依赖委托(非C# delegate,只是概念)将调料的价钱加上去

利用装饰者模式
首先我们修改主体饮料Beverage基类,GetDescription 返回描述,Cost由子类自己实现定价。
public abstract class Beverage
{
string description = "Unkonwn Beverage";
public virtual string GetDescription() {
return description;
}
public abstract float Cost();
}
深焙咖啡类
public class DarkRoast : Beverage
{
public DarkRoast() {
description = "深焙咖啡";
}
public override float Cost()
{
return 1.1f;
}
}
装饰类我们定义一个抽象基类Condiment(调料)并继承Beverage。
为什么要定义一个抽象装饰基类,因为装饰类可能需要抽象出其他方法,而且因为我们用装饰类去装饰了被装饰者后 我们本身也应该变成可已让别人装饰的类。所以装饰类继承CondimentDecorator,CondimentDecorator继承的是Beverage。此例暂未加入装饰类拥有的属性和方法
public abstract class CondimentDecorator:Beverage
{ }
我们再实现两个装饰类:Mik和Suger,用变量记录被装饰者,在构造函数里面设置被装饰者。
public class Milk : CondimentDecorator
{
//用变量记录被装饰者
Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
} public override string GetDescription()
{
return beverage.GetDescription() + "+Milk";
}
public override float Cost()
{
return beverage.Cost() + 1;
}
} public class Suger : CondimentDecorator
{
//用变量记录被装饰者
Beverage beverage;
public Suger(Beverage beverage)
{
this.beverage = beverage;
} public override string GetDescription()
{
return beverage.GetDescription() + "+Suger";
}
public override float Cost()
{
return beverage.Cost() + 2;
}
}
编写测试结果:

总结
装饰者模式说明:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
画出本例中装饰者模式类图以便理解和记忆

Head First设计模式——装饰者模式的更多相关文章
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- 从源码角度理解Java设计模式——装饰者模式
一.饰器者模式介绍 装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活. 适用场景:动态的给一个对象添加或者撤销功能. 优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个 ...
- 【设计模式】Java设计模式 - 装饰者模式
Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...
- [Head First设计模式]山西面馆中的设计模式——装饰者模式
引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧.吃 ...
- JAVA 设计模式 装饰者模式
用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式. 结构
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- javascript设计模式——装饰者模式
前面的话 在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责.那么可以使用装饰者模式.装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.本文将 ...
- 设计模式-装饰者模式(Decorator Pattern)
本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...
- C#设计模式--装饰器模式
0.C#设计模式-简单工厂模式 1.C#设计模式--工厂方法模式 2.C#设计模式--抽象工厂模式 3.C#设计模式--单例模式 4.C#设计模式--建造者模式 5.C#设计模式--原型模式 6.C# ...
随机推荐
- 装系统 ---------- 了解 UEFI与Legacy、硬盘分区MBR和GPT
UEFI:全称“统一的可扩展固件接口”(Unified Extensible Firmware Interface),一种详细描述类型接口的标准.这种接口用于操作系统自动从预启动的操作环境,加载到一种 ...
- 算法与数据结构基础 - 图(Graph)
图基础 图(Graph)应用广泛,程序中可用邻接表和邻接矩阵表示图.依据不同维度,图可以分为有向图/无向图.有权图/无权图.连通图/非连通图.循环图/非循环图,有向图中的顶点具有入度/出度的概念. 面 ...
- [Advanced Python] 14 - "Generator": calculating prime
高性能编程 几个核心问题 • 生成器是怎样节约内存的?• 使用生成器的最佳时机是什么?• 我如何使用 itertools 来创建复杂的生成器工作流?• 延迟估值何时有益,何时无益? From: htt ...
- 一篇文章了解Redis数据库
文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. redis是一个key-value存储系统.它支持存储的value类型相对更多,包括string(字符串).l ...
- php 两种获取分类树的方法
php 两种获取分类树的方法 1. /** * 获取分类树 * @param array $array 数据源 * @param int $pid 父级ID * @param int $level 分 ...
- 2020Pycharm安装教程!最新2020pycharm安装!如何安装Pycharm2020版本!如何安装Pycharm!如何安装2020Pycharm!2020Pycharm永久激活!
本教程仅用作个人学习,请勿用于商业获利,造成后果自负!!! Pycharm安装 在这插一个小话题哈,Pycharm只是一个编译器,并不能代替Python,如果要使用Python,还是需要安装Pytho ...
- 将maven项目导入到eclipse中
一,前言 本文来演示一下如何将一个新的maven项目到入到eclipse中. 在文章使用命令行创建maven web项目中我们使用maven命令行,创建了web工程,接下来为了开发方便我要将新建的工程 ...
- 程序员写 2000 行 if else?领导:这个锅我不背
前言 知乎上有小伙伴提了这么一个问题,如何看待陕西省普通话水平测试成绩查询系统?查询系统前端代码就直接给出了身份账号,姓名,证书编号,如果信息是真的,就泄露了这么多考生的信息,白给那种.为什么会发生这 ...
- Apache和Tomcat 配置负载均衡(mod-proxy方式)-粘性session
Tomcat集群配置后端Tomcat Server为支持AJP的独立服务,前端Apache配置为粘性会话(sticky-session),Tomcat不配置Cluster配置和Session复制. 配 ...
- Python邮件发送功能
import smtplibfrom email.mime.text import MIMEText_user = "1147016115@qq.com"#发件人_pwd = &q ...