装饰者模式

背景是有一家星巴兹咖啡店,由于客源充足,所以决定重新设计他们的收费系统,以前的收费系统中只定义了一个表示饮料的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:装饰者模式的更多相关文章

  1. JAVA设计模式之【装饰者模式】

    JAVA设计模式之[装饰者模式] 装饰模式 对新房进行装修并没有改变房屋的本质,但它可以让房子变得更漂亮.更温馨.更实用. 在软件设计中,对已有对象(新房)的功能进行扩展(装修). 把通用功能封装在装 ...

  2. Java设计模式12:装饰器模式

    装饰器模式 装饰器模式又称为包装(Wrapper)模式.装饰器模式以多客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰器模式的结构 通常给对象添加功能,要么直接修改对象添加相应的功能, ...

  3. 【读书笔记】读《JavaScript设计模式》之装饰者模式

    一.定义 装饰者模式可用来透明地把对象包装在具有同样接口的另一个对象之中.这样一来,你可以给一个方法添加一些行为,然后将方法调用传递给原始对象.相对于创建子类来说,使用装饰者对象是一种更灵活的选择(装 ...

  4. 设计模式学习心得<装饰器模式 Decorator>

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装 ...

  5. 设计模式のDecoratorPattern(装饰器模式)----结构模式

    一.产生背景 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装 ...

  6. 设计模式C++实现——装饰者模式

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/walkerkalr/article/details/28633123 模式定义:         装 ...

  7. 重学 Java 设计模式:实战装饰器模式(SSO单点登录功能扩展,增加拦截用户访问方法范围场景)

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 对于代码你有编程感觉吗 很多人写代码往往是没有编程感觉的,也就是除了可以把功能按照固 ...

  8. 《Head First 设计模式》之装饰者模式

    作者:Grey 原文地址:http://www.cnblogs.com/greyzeng/p/5922248.html 模式名称 装饰者模式(Decorator Pattern) 需求 定义咖啡厅中的 ...

  9. 设计模式学习之装饰者模式(Decorator,结构型模式)(16)

    参考地址:http://www.cnblogs.com/zhili/p/DecoratorPattern.html 一.定义:装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任,装饰者模式相 ...

  10. [设计模式] javascript 之 装饰者模式

    装饰者模式说明 说明:通一个类来动态的对另一个类的功能对象进行前或后的修饰,给它辅加一些额外的功能; 这是对一个类对象功能的装饰,装饰的类跟被装饰的类,要求拥有相同的访问接口方法(功能),这在动态面向 ...

随机推荐

  1. 处理HTML5新标签的浏览器兼容问题

    <!--[if lt IE 9]> <script type="text/javascript" src="js/html5shiv.js"& ...

  2. echarts修改上下左右的边距

    grid: {                top: '4%',                left: '3%',                right: '4%',            ...

  3. 用H5开发微信还是开发APP?

    用H5开发微信还是开发APP? 随着技术的飞速发展,HTML第五版技术标准的更新,在移动端,由于其相对较低的开发成本及强大的跨平台运行能力,越来越多的信息型产品也开始选择这样轻量级的H5页面进行快速迭 ...

  4. nfs+rsync备份

    我们先来做rsync 环境说明:1.操作系统:centos7 2.IP:192.168.7.226简称A&192.168.7.174 简称B 3.192.168.7.226做rsync的客户端 ...

  5. AJAX问题 XMLHttpRequest.status = 0是什么含义

    在调用AJAX的时候遇到了XMLHttpRequest. status为0 的情况,http协议里可是没这个状态码的,众所周知,XMLHttpRequest. Status为HTTP请求状态码,一般为 ...

  6. python subprocess.Popen 控制台输出 实时监控百度网ping值

    import subprocess file_out = subprocess.Popen('ping www.baidu.com', shell=True, stdout=subprocess.PI ...

  7. [2] TensorFlow 向前传播算法(forward-propagation)与反向传播算法(back-propagation)

    TensorFlow Playground http://playground.tensorflow.org 帮助更好的理解,游乐场Playground可以实现可视化训练过程的工具 TensorFlo ...

  8. Android 6.0以后的版本报错:open failed: EACCES (Permission denied)

    Android 6.0以后的版本报错:open failed: EACCES (Permission denied) 在开发项目中,遇见要进行文件操作,遇见Caused by: android.sys ...

  9. 路飞学城-Python开发集训-第3章

    学习心得: 通过这一章的作业,使我对正则表达式的使用直接提升了一个level,虽然作业完成的不怎么样,重复代码有点多,但是收获还是非常大的,有点找到写代码的感觉了,遗憾的是,这次作业交过,这次集训就结 ...

  10. selenium:解决页面元素display:none的方法

    在UI自动化测试中,有时候会遇到页面元素无法定位的问题,包括xpath等方法都无法定位,是因为前端元素被设置为不可见导致. 这篇博客,介绍下如何通过JavaScript修改页面元素属性来定位的方法.. ...