装饰者模式

背景是有一家星巴兹咖啡店,由于客源充足,所以决定重新设计他们的收费系统,以前的收费系统中只定义了一个表示饮料的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. CSS鼠标悬浮DIV后显示DIV外的按钮

    昨天写样式遇到个问题,如何让鼠标悬浮DIV后,显示DIV外的按钮,可以点击到按钮. 效果如下: 问题: 在DIV hover时候将按钮设为display: block,这是很直接的想法,但是这有个问题 ...

  2. Django admin注册model究竟要怎么写才优雅

    比如在Django admin 注册models时,会用到. 对于APP里自带的models,可以使用这种方式注册. from django.contrib import admin # Regist ...

  3. ServletContextListener的作用

    ServletContextListener是对ServeltContext的一个监听.servelt容器启动,serveltContextListener就会调用contextInitialized ...

  4. 自然语言处理之word2vec

    在word2vec出现之前,自然语言处理经常把字词转为one-hot编码类型的词向量,这种方式虽然非常简单易懂,但是数据稀疏性非常高,维度很多,很容易造成维度灾难,尤其是在深度学习中:其次这种词向量中 ...

  5. 关于CentOS7下docker-ce无法删除镜像的问题

    未完待续.... 从旧版的docker删除后安装了新版的docker-ce 发现之前镜像无法删除,并且重新pull不能覆盖,会出现两个一模一样的镜像. [root@localhost ~]# dock ...

  6. node.js之Cookie

    最近还是用node.js比较多,今天正好遇见一个问题,还是关于Cookie. node.js中如何实现cookie(以express框架为例): "use strict"; var ...

  7. Linux内核入门到放弃-进程管理和调度-《深入Linux内核架构》笔记

    进程优先级 硬实时进程 软实时进程 普通进程 O(1)调度.完全公平调度器 抢占式多任务处理(preemptive multitasking):各个进程都分配到一定的时间段可以执行.时间段到期后,内核 ...

  8. C# System.Guid.NewGuid() 格式化

    概念 GUID: 即Globally Unique Identifier(全球唯一标识符) 也称作 UUID(Universally Unique IDentifier) . GUID是一个通过特定算 ...

  9. Spring Boot 之整合 EasyUI 打造 Web 应用

    SpringBootTutorial :: Web :: UI :: EasyUI EasyUI 是一个简单的用户界面组件的集合.由于 EasyUI 已经封装好大部分 UI 基本功能,能帮用户减少大量 ...

  10. 调试CAS源码步骤

    1.先安装gradle2.eclipse安装gradle(sts)插件3.克隆cas源码 这一块需要很长时间4.gradle build 会遇到安装node.js 的模块 不存在的问题. 按提示解决就 ...