C#设计模式之九装饰模式(Decorator)【结构型】
一、引言
今天我们要讲【结构型】设计模式的第三个模式,该模式是【装饰模式】,英文名称:Decorator Pattern。我第一次看到这个名称想到的是另外一个词语“装修”,我就说说我对“装修”的理解吧,大家一定要看清楚,是“装修”,不是“装饰”。我们长大了,就要结婚,要结婚就涉及到要买房子,买的精装修或者简单装修就可以住的,暂时不谈。我们就谈谈我们购买的是毛坯房。如果我想要房子的内饰是大理石风格的,我们只要在毛坯房的基础之上用大理石风格的材料装修就可以,我们当然不可能为了要一个装修风格,就把刚刚盖好的房子拆了在重新来过。房子装修好了,我们就住了进来,很开心。过了段时间,我们发现我们的房子在冬季比较冷,于是我就想给我们的房子增加保暖的功能,装修好的房子我们可以继续居住,我们只是在房子外面加一层保护层就可以了。又过了一段时间,总是有陌生人光顾,所以我们想让房子更安全,于是我们在外墙和房顶加装安全摄像头,同时门窗也增加安全系统。随着时间的流逝,我们可能会根据我们的需求增加相应的功能,期间,我们的房子可以正常使用,加上什么设施就有了相应的功能。从这一方面来讲,“装修”和“装饰”有类似的概念,接下来就让我们看看装饰模式具体是什么吧!
二、装饰模式的详细介绍
2.1、动机(Motivate)
在房子装修的过程中,各种功能可以相互组合,来增加房子的功用。类似的,如果我们在软件系统中,要给某个类型或者对象增加功能,如果使用“继承”的方案来写代码,就会出现子类暴涨的情况。比如:IMarbleStyle是大理石风格的一个功能,IKeepWarm是保温的一个接口定义,IHouseSecurity是房子安全的一个接口,就三个接口来说,House是我们房子,我们的房子要什么功能就实现什么接口,如果房子要的是复合功能,接口不同的组合就有不同的结果,这样就导致我们子类膨胀严重,如果需要在增加功能,子类会成指数增长。这个问题的根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质(所谓静态特质,就是说如果想要某种功能,我们必须在编译的时候就要定义这个类,这也是强类型语言的特点。静态,就是指在编译的时候要确定的东西;动态,是指运行时确定的东西),使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态(即运行时)地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?
2.2、意图(Intent)
动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。 —— 《设计模式》GoF
2.3、结构图(Structure)
2.4、模式的组成
在装饰模式中的各个角色有:
(1)、抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象。
(2)、具体构件角色(Concrete Component):定义一个将要接收附加责任的类。
(3)、装饰角色(Decorator):持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
(4)、具体装饰角色(Concrete Decorator):负责给构件对象添加上附加的责任。
2.5 、装饰模式的具体代码实现
刚开始一看这个“装饰模式”是有点不太好理解,既然这个模式是面向对象的设计模式,那在现实生活中一定有事例和其对应,其实这种例子也不少,大家好好的挖掘吧,也可以提高我们对面向对象的理解。我继续拿盖房子来说事吧。
namespace 装饰模式的实现
{
/// <summary>
/// 该抽象类就是房子抽象接口的定义,该类型就相当于是Component类型,是饺子馅,需要装饰的,需要包装的
/// </summary>
public abstract class House
{
//房子的装修方法--该操作相当于Component类型的Operation方法
public abstract void Renovation();
} /// <summary>
/// 该抽象类就是装饰接口的定义,该类型就相当于是Decorator类型,如果需要具体的功能,可以子类化该类型
/// </summary>
public abstract class DecorationStrategy:House //关键点之二,体现关系为Is-a,有这这个关系,装饰的类也可以继续装饰了
{
//通过组合方式引用Decorator类型,该类型实施具体功能的增加
//这是关键点之一,包含关系,体现为Has-a
protected House _house; //通过构造器注入,初始化平台实现
protected DecorationStrategy(House house)
{
this._house=house;
} //该方法就相当于Decorator类型的Operation方法
public override void Renovation()
{
if(this._house!=null)
{
this._house.Renovation();
}
}
} /// <summary>
/// PatrickLiu的房子,我要按我的要求做房子,相当于ConcreteComponent类型,这就是我们具体的饺子馅,我个人比较喜欢韭菜馅
/// </summary>
public sealed class PatrickLiuHouse:House
{
public override void Renovation()
{
Console.WriteLine("装修PatrickLiu的房子");
}
} /// <summary>
/// 具有安全功能的设备,可以提供监视和报警功能,相当于ConcreteDecoratorA类型
/// </summary>
public sealed class HouseSecurityDecorator:DecorationStrategy
{
public HouseSecurityDecorator(House house):base(house){} public override void Renovation()
{
base.Renovation();
Console.WriteLine("增加安全系统");
}
} /// <summary>
/// 具有保温接口的材料,提供保温功能,相当于ConcreteDecoratorB类型
/// </summary>
public sealed class KeepWarmDecorator:DecorationStrategy
{
public KeepWarmDecorator(House house):base(house){} public override void Renovation()
{
base.Renovation();
Console.WriteLine("增加保温的功能");
}
} public class Program
{
static void Main()
{
//这就是我们的饺子馅,需要装饰的房子
House myselfHouse=new PatrickLiuHouse(); DecorationStrategy securityHouse=new HouseSecurityDecorator(myselfHouse);
securityHouse.Renovation();
//房子就有了安全系统了 //如果我既要安全系统又要保暖呢,继续装饰就行
DecorationStrategy securityAndWarmHouse=new HouseSecurityDecorator(securityHouse);
securityAndWarmHouse.Renovation();
}
}
}
写了很多备注,大家好好体会一下,里面有两个关键点,仔细把握。
三、装饰模式的实现要点:
1、通过采用组合、而非继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。
2、Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明——换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。
3、Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。
4、Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
3.1】、装饰模式的优点:
(1)、把抽象接口与其实现解耦。
(2)、抽象和实现可以独立扩展,不会影响到对方。
(3)、实现细节对客户透明,对用于隐藏了具体实现细节。
3.2】、装饰模式的缺点:
(1)、增加了系统的复杂度
3.3】、在以下情况下应当使用桥接模式:
(1)、如果一个系统需要在构件的抽象化角色和具体化角色之间添加更多的灵活性,避免在两个层次之间建立静态的联系。
(2)、设计要求实现化角色的任何改变不应当影响客户端,或者实现化角色的改变对客户端是完全透明的。
(3)、需要跨越多个平台的图形和窗口系统上。
(4)、一个类存在两个独立变化的维度,且两个维度都需要进行扩展。
四、.NET 中装饰模式的实现
在Net框架中,有一个类型很明显的使用了“装饰模式”,这个类型就是Stream。Stream类型是一个抽象接口,它在System.IO命名空间里面,它其实就是Component。FileStream、NetworkStream、MemoryStream都是实体类ConcreteComponent。右边的BufferedStream、CryptoStream是装饰对象,它们都是继承了Stream接口的。
如图:
Stream就相当于Component,定义装饰的对象,FileStream就是要装饰的对象,BufferedStream是装饰对象。我们看看BufferedStream的定义,部分定义了。
public sealed class BufferedStream : Stream
{
private const int _DefaultBufferSize = ; private Stream _stream;
结构很简单,对比结构图看吧,没什么可说的了。
五、总结
今天的文章就写到这里了,总结一下我对这个模式的看法,这个模式有点像包饺子,ConcreteComponent其实是饺子馅,Decorator就像饺子皮一样,包什么皮就有什么的样子,皮和皮也可以嵌套,当然我们生活中的饺子只是包一层。其实手机也是一个装饰模式使用的好例子,以前我们的手机只是接打电话,然后可以发短信和彩信,我在装饰一个就可以拍照了。我们现在的手机功能很丰富,其结果也类似装饰的结果。随着社会的进步,技术发展,模块化的手机也出现了,其设计原理也和“装饰模式”就更接近了。不光手机,还有我们身边其他很多家用电器也有类似的发展经历,我们努力发现生活中的真理吧,然后再在软件环境中慢慢体会吧。
C#设计模式之九装饰模式(Decorator)【结构型】的更多相关文章
- 设计模式09: Decorator 装饰模式(结构型模式)
Decorator 装饰模式(结构型模式) 子类复子类,子类何其多加入我们需要为游戏中开发一种坦克,除了不同型号的坦克外,我们还希望在不同场合中为其增加以下一种多种功能:比如红外线夜视功能,比如水路两 ...
- 修饰模式(Decorator结构型)C#简单例子
修饰模式(Decorator结构型)C#简单例子 玩家基本功能是移动.运行等等.BaseAbility新增加功能:1.伤害技能harmAbility:2.阻碍技能BaulkAbility:3.辅助技能 ...
- .NET设计模式(15):结构型模式专题总结(转)
摘要:结构型模式,顾名思义讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象,从而实现新的功能(对象结构型模式).这些结构型模式,它们在某些方面具有很大的相似 ...
- 设计模式系列之装饰模式(Decorator Pattern)
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装.这种模式创建了一个装饰类,用来包装原 ...
- 设计模式系列之装饰模式(Decorator Pattern)——扩展系统功能
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- JAVA设计模式(09):结构型-代理模式(Proxy)
代理模式是经常使用的结构型设计模式之中的一个,当无法直接訪问某个对象或訪问某个对象存在困难时能够通过一个代理对象来间接訪问,为了保证client使用的透明性,所訪问的真实对象与代理对象须要实现同样的接 ...
- 设计模式理解(十)结构型——享元(Flyweight)
最后一个结构型,享元.没有太多的项目经验,对这种模式只有一种概念上的理解,就是为了节约内存等资源,把可重用的东西只申请一次,然后处处调用,同时用Hash进行管理. 直接上图: 代码: /******* ...
- 装饰器模式 Decorator 结构型 设计模式 (十)
引子 现实世界的装饰器模式 大家应该都吃过手抓饼,本文装饰器模式以手抓饼为模型展开简介 "老板,来一个手抓饼, 加个培根, 加个鸡蛋,多少钱?" 这句话会不 ...
- 设计模式-Decorator(结构型模式) 用于通过 组合 的方式 给定义的类 添加新的操作,这里不用 继承 的原因是 增加了系统的复杂性,继承使深度加深。
以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Decorator.h #pragma once class Component { public: virtual ~C ...
随机推荐
- 201521123054《java程序设计》第12周学习总结
1. 本周学习总结 2. 书面作业 1. 字符流与文本文件:使用 PrintWriter(写),BufferedReader(读) 1.1 生成的三个学生对象,使用PrintWriter的printl ...
- 201521123110《Java程序设计》第10周学习总结
1. 本周学习总结 2. 书面作业 1.finally 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? finally块中的代码在正常情况下一定会执行,所 ...
- XML功能
REF:https://www.baidu.com/link?url=_-UY8rZVAORlesKTth0F7C8LbvXCL4lSwz6vmQVnTEgmT06NFGdoaD9FbuEQhR7xG ...
- 警惕Java编译器中那些“蜜糖”陷阱
一.前言 随着Java编译器不断地向前发展,它为程序员们提供了越来越多的“蜜糖”(compiler suger),极大地方便了程序的开发,例如,foreach的增强模式,自动拆箱与装箱以及字符串的连接 ...
- 协议端口号(protocol port number)
协议端口号(protocol port number) 先来个注意事项 (-> ->) 这种在协议层间的抽象的协议端口是软件端口,和硬件端口是完全不同的概念.硬件端口是不同设备进行交互的接 ...
- 阿里云服务器解决mysql远程连接失败问题
嗯,自己买了个阿里云的学生机服务器,奈何装了mysql以后一直不能连接,也是够笨的. 记录一下自己遇到的问题. 当然了,首先需要在阿里云安全组开放3306端口,第一次玩儿云服务器差点把我搞坏了.... ...
- 【小程序】微信小程序实现各种特效实例
写在前面 最近在负责一个微信小程序的前端以及前后端接口的对接的项目,整体上所有页面的布局我都已经搭建完成,里面有一些常用的特效,总结一下,希望对大家和我都能有所帮助 实例1:滚动tab选项卡 先看一下 ...
- 01背包java实现(入门到精通)
一.什么是01背包 01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn.01背包是背包问题中最简单的问题.01背包的约束条件是给定 ...
- TeamFlowy——结合Teambition与Workflowy提高生产力
Teambition是一个跨平台的团队协作和项目管理工具,相当于国外的Trello.使用Teambition可以像使用白板与便签纸一样来管理项目进度,如下图所示. Teambition虽然便于管理项目 ...
- uva12519
The Farnsworth Parabox Professor Farnsworth, a renowned scientist that lives in year 3000 working at ...