装饰者模式

背景是有一家星巴兹咖啡店,由于客源充足,所以决定重新设计他们的收费系统,以前的收费系统中只定义了一个表示饮料的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. bsp 总结

    _board_128.c里放硬件不同的东西,如gpio等 product下code里面的cspkernel里面放内核模块补充的

  2. grep正则表达式搜索

    grep -n -e "INT32 *AdaptorPrmOp" --include "*.c"  -r ./ 搜索函数的定义 中间有n个空格

  3. Announcing the Updated NGINX and NGINX Plus Plug‑In for New Relic (Version 2)

    In March, 2013 we released the first version of the “nginx web server” plug‑in for New Relic monitor ...

  4. Eclipse debug 断点不能调试 ,Eclipse Unable to install breakpoint in 解决办法

    解决:[1]项目工程名 ,右键 --> properties --> java compiler -->class file Generation 位置  Add line numb ...

  5. JSP内置九个对象Request请求对象

    jsp内置对象是什么呢? 例如Java语言使用一个对象之前需要实例化(也就是所说的new一个对象),创建对象这个过程有点麻烦,所以在jsp中提供了一些内置对象,用来实现很多jsp应用.在使用内置对象时 ...

  6. 在PHP中管理环境变量

    在PHP中管理环境变量 现在我们都能用很多个编程语言开发,当我开始熟悉PHP时,我会忽略其它语言的特点.我用过其他语言(比如Node.js),但在PHP中没有看到一种轻松控制设置环境变量的方法,特别是 ...

  7. Insert Into 语句的语法错误

    错误示意: 一开始程序是: 改正: 一条很简单的插入语句竟然会报错,然而直接在数据库的查询中执行中却没有问题,这个问题困扰了不少时间. 数据库使用的是ACCESS,INSERT INTO语句为inse ...

  8. Mysql SQL分组取每组前几条记录

    按name分组取最大的两个val: [比当前记录val大的条数]小于2条:即当前记录为为分组中的前两条 > (select count(*) from tb where name = a.nam ...

  9. [SDOI2008]递归数列

    嘟嘟嘟 裸的矩阵快速幂,构造一个\((k + 1) * (k + 1)\)的矩阵,把sum[n]也放到矩阵里面就行了. #include<cstdio> #include<iostr ...

  10. 17 python 初学(迭代器)

    生成器都是迭代器,迭代器不一定是生成器 迭代器满足两个条件: 1. 有iter方法 2. 有next方法 # list, tuple, dict, string: iterable(他们都有iter方 ...