C#设计模式之4:装饰者模式
装饰者模式
背景是有一家星巴兹咖啡店,由于客源充足,所以决定重新设计他们的收费系统,以前的收费系统中只定义了一个表示饮料的Beverage的基类,它里面定义了一个Cost的方法用来计算饮料的花费,但是对于星巴兹来说他们的饮料的种类实在太多了,不能就每一种饮料就建立一个子类,类型爆炸!
所以要进行一番设计,来改变目前这种类型爆炸的局面。
利用继承设计子类的行为,实在编译时静态决定的,而且所有的子类都会继承到相同的行为,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态的进行扩展。通过动态的组合对象,可以写新的代码添加新功能,而无须修改现有代码。既然没有改变现有代码,那么引进bug或者产生意外副作用的机会就会变得少很多。
设计原则:类应该对扩展开放,对修改关闭(开闭原则)
我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为,如果实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。
遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度。需要把注意力集中在设计中最有可能改变的地方,然后应用开放-关闭原则。
按照上述原则分析需求,首先,我们把饮料当作主体,而各种调料当作装饰,用调料来装饰饮料,如果顾客想要摩卡和奶泡咖啡,那么要做的是:
①准备一个咖啡对象。
②用摩卡装饰他。
③用奶泡装饰他。
④调用cost方法。
在给出具体的做法之前,先给出装饰者模式的定义:动态的将责任附加到对象上,如要扩展功能,装饰者提供了比继承更有弹性的替代方案。
简单的来说,就是用Decorator来装饰ConcreteComponent,因为他们都具有相同的基类,达到了一种“类型匹配”的目的,而不是用来获取基类的行为。到时候,相关的类型之间能够被互相取代。
当我们将装饰着和组件组合时,就是在加入新的行为,所得到的新的行为,并不是继承得来的,而是有组合对象得来的。如果依赖继承,那么类的行为就是在编译时静态决定的。换句话说,行为如果不是来自基类,就是来自子类覆盖后的版本。反之,如果利用组合,可以把装饰着混合着用,而且是在运行时。
而且还可以在任何时候实现新的装饰者增加新的行为,如果依赖继承,每当需要新行为时,还得修改现有的代码。
设计流程
按照上面给的UML类图,首先从Beverage下手,这代表了一个Component,所有的类型都直接或者间接的从它继承,来获取类型的一致性。而这是Decorator的关键。
public abstract class Beverage
{
public virtual string Description { get; set; }
public abstract double Cost();
}
接着,定义一个ConcreteComponent:
public sealed class Espresso:Beverage//代表某种咖啡
{
public Espresso()
{
Description = "Espresso";
}
public override double Cost()
{
return 1.2D;
}
}
由于在Beverage中Cost是抽象方法,所以要在子类中去override。
再定义一个装饰者的基类,也就是类图上的CondimentDecorator:
public abstract class CondimentDecorator : Beverage
{
public override string Description { get; set; }
}
然后就是定义具体的装饰类:
public class Mocha:CondimentDecorator//摩卡,一种咖啡的“装饰”
{
private readonly Beverage _beverage; public Mocha(Beverage beverage)
{
this._beverage = beverage;
}
public override string Description
{
get => _beverage.Description + "," + " Mocha";
set => base.Description = value; }
public override double Cost()
{
return 1.5 + _beverage.Cost();
}
}
然后测试一下:
class Program
{
static void Main(string[] args)
{
Beverage beverage=new Espresso();
Console.WriteLine($"{beverage.Description},Cost is {beverage.Cost()}");
beverage=new Mocha(beverage);//进一步装饰
Console.WriteLine($"{beverage.Description},Cost is {beverage.Cost()}");
beverage=new Mocha(beverage);//进一步装饰
Console.WriteLine($"{beverage.Description},Cost is {beverage.Cost()}");
Console.ReadKey();
}
}
从上述代码可以看到这种装饰的行为正是依赖了统一的基类带来的类型的一致性,也证实了我们前面所言非虚。而在具体类的内部,根据对象的组合进一步扩展了对象的功能。由于对象组合上是针对抽象(抽象类)进行编程,所以很容易通过IOC容器来进行控制反转的实现。
C#设计模式之4:装饰者模式的更多相关文章
- JAVA设计模式之【装饰者模式】
JAVA设计模式之[装饰者模式] 装饰模式 对新房进行装修并没有改变房屋的本质,但它可以让房子变得更漂亮.更温馨.更实用. 在软件设计中,对已有对象(新房)的功能进行扩展(装修). 把通用功能封装在装 ...
- Java设计模式12:装饰器模式
装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...
- 【读书笔记】读《JavaScript设计模式》之装饰者模式
一.定义 装饰者模式可用来透明地把对象包装在具有同样接口的另一个对象之中.这样一来,你可以给一个方法添加一些行为,然后将方法调用传递给原始对象.相对于创建子类来说,使用装饰者对象是一种更灵活的选择(装 ...
- 设计模式学习心得<装饰器模式 Decorator>
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装 ...
- 设计模式のDecoratorPattern(装饰器模式)----结构模式
一.产生背景 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装 ...
- 设计模式C++实现——装饰者模式
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/walkerkalr/article/details/28633123 模式定义: 装 ...
- 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...
- 《Head First 设计模式》之装饰者模式
作者:Grey 原文地址:http://www.cnblogs.com/greyzeng/p/5922248.html 模式名称 装饰者模式(Decorator Pattern) 需求 定义咖啡厅中的 ...
- 设计模式学习之装饰者模式(Decorator,结构型模式)(16)
参考地址:http://www.cnblogs.com/zhili/p/DecoratorPattern.html 一.定义:装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任,装饰者模式相 ...
- [设计模式] javascript 之 装饰者模式
装饰者模式说明 说明:通一个类来动态的对另一个类的功能对象进行前或后的修饰,给它辅加一些额外的功能; 这是对一个类对象功能的装饰,装饰的类跟被装饰的类,要求拥有相同的访问接口方法(功能),这在动态面向 ...
随机推荐
- 在已经安装的nginx上,增加ssl模块
1. /usr/local/nginx/sbin/nginx -V 查看nginx版本与编译安装了哪些模块nginx version: nginx/1.10.3built by gcc 4.4.7 2 ...
- Automatically migrating data to new machines kafka集群扩充迁移topic
The partition reassignment tool can be used to move some topics off of the current set of brokers to ...
- 小程序request封装
我们知道,一个项目开发时,有许多环境,如:开发环境,测试环境,预生产环境,生成环境,若项目上线时要每个接口的域名改一遍,这是效率很低的做法.另外,许多接口都有前缀,例如 /api/ /wxapi/ ...
- Numpy 模块的应用
数据分析三剑客: Numpy, Pandas, Matplotlib NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数 ...
- 【足迹C++primer】32、定制操作_2
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/cutter_point/article/details/32301839 定制操作_2 完整的big ...
- VBA果然很强大
1.我的是office 2007,新建空白文档-开发工具 -visual basic -project -thisDocument - Document_Open ,写入以下代码(并另存为word ...
- tensorflow中的Supervisor
tf.train.Supervisor()可以帮我们简化一些事情,可以保存模型参数和Summary,它有以下的作用: 1)自动去checkpoint加载数据或初始化数据 ,因此我们就不需要手动初始化或 ...
- 六、Oracle 存储过程
一.存储过程1语法:create procedure 名字is|as 声明变量begin 代码块end; 2.执行存储过程打开命令窗口,输入:exec 过程名字 3.打开输出命令:set server ...
- 1.java的基础和数据类型
一.学习要求1.听课一定要全神贯注2.课堂笔记,一定要自己总结,而且要有很严谨的逻辑关系.提纲很重要3.作业不折不扣的完成,并且多完成4.阶段项目一定要独立完成5.每天早上由一位同学来进行早分享,内容 ...
- (1) 安卓导入mqtt包基本通信
参考资料:http://blog.csdn.net/qq_17250009/article/details/52774472 MQTT官网:http://mqtt.org/ MQTT介绍:http:/ ...