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# ...
随机推荐
- 第二场周赛(递归递推个人Rank赛)——题解
很高兴给大家出题,本次难度低于上一场,新生的六个题都可以直接裸递归式或者裸递推式解决,对于老生的汉诺塔3,需要找出一般式,后两题分别为裸ST算法(或线段树)/线性DP. 正确的难度顺序为 种花 角谷定 ...
- [3]尝试用Unity3d制作一个王者荣耀(持续更新)->选择英雄-(中)
如果已经看过本章节:目录传送门:这是目录鸭~ 上节内容写了Actor管理器,那么这一节让我们先创建一个角色.(此章节开始加速...) 1.制作角色展示AssetBundle: 提取农药某个展示模型(S ...
- CSS——边框设置
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Linux 中 Xampp 的 https 安全证书配置
博客地址:http://www.moonxy.com 一.前言 HTTP 协议是不加密传输数据的,也就是用户跟你的网站之间传递数据有可能在途中被截获,破解传递的真实内容,所以使用不加密的 HTTP 的 ...
- Day 7 vim 文件编辑与vim的使用
1.什么是vim? vi 和 vim 是Linux下的一个文本编辑工具,(可以理解为windows的记事本或word文档) 2.为什么要使用vim? 因为linux 系统一切皆为文件,而我们工作最多的 ...
- 年年有余之java求余的技巧集合
背景 传说里玉皇大帝派龙王马上降雨到共光一带,龙王接到玉皇大帝命令,立马从海上调水,跑去共光施云布雨,但粗心又着急的龙王不小心把海里的鲸鱼随着雨水一起降落在了共光,龙王怕玉皇大帝责怪,灵机一动便声称他 ...
- 基于Spark的电影推荐系统(实战简介)
写在前面 一直不知道这个专栏该如何开始写,思来想去,还是暂时把自己对这个项目的一些想法 和大家分享 的形式来展现.有什么问题,欢迎大家一起留言讨论. 这个项目的源代码是在https://github. ...
- CDH 5.9.3 集群配置
-----------------------------------------集群规划------------------------------------------ hostname ip ...
- 阿里云短信服务开发报错Java.lang.NoClassDefFoundError:com/aliyuncs/exceptions/ClientException
手机app获取短信验证码功能时候,遇到的问题.使用的是阿里云的短信服务,下载平台demo时运行不报错,但是在service层调用的时候报错 Java.lang.NoClassDefFoundError ...
- SUSE Linux Enterprise 15 SP1 系统安装
从 SUSE Linux Enterprise Server 15 开始,安装媒体仅包含安装程序 - 一个用于安装.更新和注册 SUSE Linux Enterprise Server 的基于命令行的 ...