C#设计模式学习笔记:(8)装饰模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7723225.html,记录一下学习过程以备后续查用。
一、引言
今天我们要讲结构型设计模式的第三个模式--装饰模式。当第一次看到这个名称时想到的是另外一个词语“装修”,个人观点谈谈对“装修”的理解吧,请大家
看清楚现在说是“装修”而不是“装饰”。当我们长大了就要准备结婚(男大当婚女大当嫁嘛),而结婚往往涉及到要买房的事。如果买的是毛坯房,假如想要房
子的内饰是大理石风格的,我们只需在毛坯房的基础之上用大理石风格的材料装修就可以(不需要重新盖房)。房子装修好了住了进来很开心,过了段时间,
发现房子在冬季比较冷,于是就想给房子增加保暖的功能,此时我们只需在房子里面增加采暖系统(南方人使用变频空调)。又过了一段时间,总是有陌生
人光顾,于是想给房子增加安防设备,此时我们可以在门口及室内加装监控摄像头(或者加红外、门磁等布控)及安装防盗网。随着时间的流逝,我们可能
会根据我们的需求增加相应的功能,而在此期间,我们的房子都是可以正常使用的,从这一方面来讲,“装修”和“装饰”有类似的概念。下面让我们看看装饰模
式具体是什么吧?
二、装饰模式介绍
装饰模式:英文名称--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 、装饰模式的具体实现
继续拿房子的例子来说吧:
class Program
{
/// <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)
{
_house = house;
} //该方法就相当于Decorator类型的Operation方法。
public override void Renovation()
{
if (_house != null)
{
_house.Renovation();
}
}
} /// <summary>
/// 我的房子,相当于ConcreteComponent类型。
/// </summary>
public sealed class MyHouse : House
{
public override void Renovation()
{
Console.WriteLine("装修我的房子,比如大理石风格。");
}
} /// <summary>
/// 增加保暖功能,相当于ConcreteDecoratorA类型。
/// </summary>
public sealed class KeepWarmDecorator : DecorationStrategy
{
public KeepWarmDecorator(House house) : base(house) { } public override void Renovation()
{
//base.Renovation();
Console.WriteLine("增加保暖功能。");
}
} /// <summary>
/// 增加安防设备,相当于ConcreteDecoratorB类型。
/// </summary>
public sealed class SecurityDecorator : DecorationStrategy
{
public SecurityDecorator(House house) : base(house) { } public override void Renovation()
{
//base.Renovation();
Console.WriteLine("增加安防设备。");
}
} static void Main(string[] args)
{
#region 装饰模式
//需要装饰的房子
House myHouse = new MyHouse();
myHouse.Renovation(); //增加保暖功能
DecorationStrategy warmHouse = new KeepWarmDecorator(myHouse);
warmHouse.Renovation(); //如果房子既要保暖又要安防,继续装饰就行。
DecorationStrategy warmAndSecurityHouse = new SecurityDecorator(warmHouse);
warmAndSecurityHouse.Renovation(); Console.Read();
#endregion
}
}
写了很多备注,大家好好体会一下,里面有两个关键点,仔细把握。
运行结果如下:
三、装饰模式的实现要点
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#设计模式学习笔记:(8)装饰模式的更多相关文章
- 设计模式学习笔记——Decorator装饰模式
装饰模式的作用或动机就是,尽量避免继承,而使用关联.原因是层层继承下来,内容会越来越多,有失控的危险.就扩展性而言,用关联比用继承好.所谓的关联,A使用了B,就叫A关联了B. Component 抽象 ...
- C#设计模式学习笔记-单例模式随笔
最近学习 设计模式,从单例模式入手 啥是单例模式: 要实现一个单例类的话,首先,肯定是不能让用户自行生产的,那就是说明不能让用户new,所以,就必须把构造函数设置成为私有的 因为静态变量的生命周期跟整 ...
- 设计模式学习笔记--备忘录(Mamento)模式
写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...
- C#设计模式学习笔记-单例模式(转)
C#设计模式学习笔记-单例模式 http://www.cnblogs.com/xun126/archive/2011/03/09/1970807.html 最近在学设计模式,学到创建型模式的时候,碰到 ...
- Java设计模式学习笔记(二) 简单工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...
- Java设计模式学习笔记(三) 工厂方法模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...
- Java设计模式学习笔记(四) 抽象工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...
- Java设计模式学习笔记(五) 单例模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 使用单例模式的原因 以Windows任务管理器为例,在Windows系统中,任务管理器是唯 ...
- 7 种 Javascript 常用设计模式学习笔记
7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...
- C#设计模式学习笔记:(3)抽象工厂模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...
随机推荐
- Android教程2020 - RecyclerView使用入门
本文介绍RecyclerView的使用入门.这里给出一种比较常见的使用方式. Android教程2020 - 系列总览 本文链接 想必读者朋友对列表的表现形式已经不再陌生.手机上有联系人列表,文件列表 ...
- qq机器人 python实现 自动回复
我以前写的代码我现在贴在了下面,下面的连接是我自己的博客,有问题希望大家提出来,一起进步...我以前试过,没啥问题.可以实现聊天. https://realwuxiong.github.io/blog ...
- Oracle监听器
Oracle 监听器 Listener 是一个重要的数据库服务器组件,在整个 Oracle 体系结构中,扮演着重要的作用.它负责管理 Oracle 数据库和客户端之间的通讯,它在一个特定的网卡端口(默 ...
- CSS学习 | 思维导图
CSS样式
- 项目SpringMVC+Spring+Mybatis 整合环境搭建(2)-> 测试Spring+Mybatis 环境
测试前期准备 第一步:创建easybuy数据库,设置utf-8格式 第二步:创建表test_tb CREATE TABLE `test_tb` ( `id` int(11) NOT NULL AUTO ...
- 奇葩报错0xc0000142
电脑突然蓝屏了一次,后来软件就打不开了,显示无法启动 网上找了一下说估计是蓝屏出现了一些问题注册表信息被删了,让输入命令重新加载一回注册表信息 for %1 in (%windir%\system32 ...
- python学习Day06--编码
[主要内容] 1. is 和 == 区别 id()函数 == 判断两边的值 is 判断内存地址回顾编码: 1. ASCII: 英文, 特殊字符, 数字, 8bit, 1byte 2. GBK: 中文 ...
- linux入门系列10--firewalld防火墙管理
上一篇文章学习了用户及文件相关权限,本篇继续学习防火墙技术. 防火墙作为公网与内网之间的保护屏障,对系统至关重要.防火墙又分为硬件防火墙和软件防火墙,主要功能都是依据设置的策略对穿越防火墙的流量进行过 ...
- 洛谷P1649 【[USACO07OCT]障碍路线Obstacle Course】
题目描述 Consider an N x N (1 <= N <= 100) square field composed of 1 by 1 tiles. Some of these ti ...
- 行人重识别和车辆重识别(ReID)中的评测指标——mAP和Rank-k
1.mAP mAP的全称是mean Average Precision,意为平均精度均值(如果按照原来的顺利翻译就是平均均值精度).这个指标是多目标检测和多标签图像分类中长常用的评测指标,因为这类任务 ...