一、动机(Motivate)

在房子装修的过程中,各种功能可以相互组合,来增加房子的功用。类似的,如果我们在软件系统中,要给某个类型或者对象增加功能,如果使用“继承”的方案来写代码,就会出现子类暴涨的情况。比如:IMarbleStyle是大理石风格的一个功能,IKeepWarm是保温的一个接口定义,IHouseSecurity是房子安全的一个接口,就三个接口来说,House是我们房子,我们的房子要什么功能就实现什么接口,如果房子要的是复合功能,接口不同的组合就有不同的结果,这样就导致我们子类膨胀严重,如果需要在增加功能,子类会成指数增长。这个问题的根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质(所谓静态特质,就是说如果想要某种功能,我们必须在编译的时候就要定义这个类,这也是强类型语言的特点。静态,就是指在编译的时候要确定的东西;动态,是指运行时确定的东西),使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态(即运行时)地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

二、意图(Intent)

动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。        ——  《设计模式》GoF

三、结构图(Structure)

四、模式的组成

在装饰模式中的各个角色有:
(1)、抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象。
(2)、具体构件角色(Concrete Component):定义一个将要接收附加责任的类。
(3)、装饰角色(Decorator):持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
(4)、具体装饰角色(Concrete Decorator):负责给构件对象添加上附加的责任。

五 、装饰模式的具体代码实现

继续拿盖房子来说事吧。

/// <summary>
/// 该抽象类就是房子抽象接口的定义,该类型就相当于是Component类型,是饺子馅,需要装饰的,需要包装的
/// </summary>
public abstract class House
{
public abstract void Renovation();//房子的装修方法--该操作相当于Component类型的Operation方法
} /// <summary>
/// 该抽象类就是装饰接口的定义,该类型就相当于是Decorator类型,如果需要具体的功能,可以子类化该类型
/// </summary>
public abstract class DecorationStrategy : House //关键点之二,体现关系为Is-a,有这这个关系,装饰的类也可以继续装饰了
{
//通过组合方式引用Decorator类型,该类型实施具体功能的增加这是关键点之一,包含关系,体现为Has-a
protected House _house; protected DecorationStrategy(House house)//通过构造器注入,初始化平台实现
{
this._house = house;
} public override void Renovation() //该方法就相当于Decorator类型的Operation方法
{
if (this._house != null)
{
this._house.Renovation();
}
}
} /// <summary>
/// PatrickLiu的房子,我要按我的要求做房子,相当于ConcreteComponent类型,这就是我们具体的饺子馅,我个人比较喜欢韭菜馅
/// </summary>
public sealed class MyHouse : 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 MyHouse();//这就是我们的饺子馅,需要装饰的房子 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模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

1、装饰模式的优点:

  1. 把抽象接口与其实现解耦。
  2. 抽象和实现可以独立扩展,不会影响到对方。
  3. 实现细节对客户透明,对用于隐藏了具体实现细节。

2、装饰模式的缺点:

  1. 增加了系统的复杂度

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 = 4096;
private Stream _stream;
}

结构型模式(三) 装饰模式(Decorator)的更多相关文章

  1. Java设计模式(9)——结构型模式之装饰模式(Decorator)

    一.概述 动态地给一个对象添加一些额外的职责.就增加功能来说, Decorator模式相比生成子类更为灵活.该模式以对客 户端透明的方式扩展对象的功能. UML简图 角色 在持有Component的引 ...

  2. 结构型设计模式之装饰模式(Decorator)

    结构 意图 动态地给一个对象添加一些额外的职责.就增加功能来说,D e c o r a t o r 模式相比生成子类更为灵活. 适用性 在不影响其他对象的情况下,以动态.透明的方式给单个对象添加职责. ...

  3. GoF23种设计模式之结构型模式之装饰模式

    一.概述 动态地给一个对象添加一些额外的职责.装饰模式比生成子类更为灵活. 二.适用性 1.在不影响其他对象的情况下,以动态.透明的方式给但个对象添加职责. 2.处理那些可以撤销的职责. 3.当不能采 ...

  4. 设计模式09: Decorator 装饰模式(结构型模式)

    Decorator 装饰模式(结构型模式) 子类复子类,子类何其多加入我们需要为游戏中开发一种坦克,除了不同型号的坦克外,我们还希望在不同场合中为其增加以下一种多种功能:比如红外线夜视功能,比如水路两 ...

  5. DesignPattern(三)结构型模式(上)

    结构型模式 结构型模式,顾名思义讨论的是类和对象的结构 ,主要用来处理类或对象的组合.它包括两种类型,一是类结构型模式,指的是采用继承机制来组合接口或实现:二是对象结构型模式,指的是通过组合对象的方式 ...

  6. 设计模式GOF23(结构型模式:代理模式,适配模式,桥接模式,组合模式,装饰模式,外观模式,享元模式)

    结构型模式: – 分类: • 适配器模式.代理模式.桥接模式.装饰模式.组合模式.外观模式.享元模式 – 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题.   结构 ...

  7. NET设计模式 第三部分 结构型模式(7):适配器模式(Adapter Pattern)

    适配器模式(Adapter Pattern) ——.NET设计模式系列之八 Terrylee,2006年2月 概述 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但 ...

  8. Java 23种设计模式详尽分析与实例解析之二--结构型模式

    Java设计模式 结构型模式 适配器模式 模式动机:在软件开发中采用类似于电源适配器的设计和编码技巧被称为适配器模式.通常情况下,客户端可以通过目标类的接口访问它所提供的服务.又是,现有的类可以满足客 ...

  9. OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式

    前言 前面我们学习了创建型设计模式,其中有5中,个人感觉比较重要的是工厂方法模式.单例模式.原型模式.接下来我将分享的是结构型模式! 一.适配器模式 1.1.适配器模式概述 适配器模式(Adapter ...

随机推荐

  1. 使用GIT上传文件,VSCODE使用GIT上传项目

    GIT使用方法: 1.安装git 2.设置用户名和邮箱: git config --global user.name="haokan1113" git config --globa ...

  2. python基础 — 异常处理

    什么是异常? 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行. 一般情况下,在Python无法正常处理程序时就会发生一个异常. 异常是Python对象,表示一个错误. 当Pyth ...

  3. 16 IO流(十三)——Object流 序列化与反序列化

    Object流.序列化与反序列化 Object流是将 可序列化的对象 进行序列化与反序列化的流. 可序列化的对象:使用关键字Serializable修饰,表示这个对象可以进行序列化与反序列化. 序列化 ...

  4. C++ 多态详解及常见面试题

    今天,讲一讲多态: 多态就是不同对象对同一行为会有不同的状态.(举例 : 学生和成人都去买票时,学生会打折,成人不会) 实现多态有两个条件: 一是虚函数重写,重写就是用来设置不同状态的   二是对象调 ...

  5. 在jenkins中使用shell命令推送当前主机上的docker镜像到远程的Harbor私有仓库

    1.jenkins主机上的docker配置 先在Jenkins主机的docke上配置上Harbor私有仓库地址 cat /etc/docker/daemon.json { "insecure ...

  6. java.lang.SecurityManager、java.security包

    学习java大概3年多了,一直没有好好研究过java安全相关的问题,总是会看到 SecurityManger sm = System.getSecurityManager(); if(sm!=null ...

  7. 广度优先搜索(BFS)思路及算法分析

    1.算法用途: 是一种图像搜索演算法.用于遍历图中的节点,有些类似于树的深度优先遍历.这里唯一的问题是,与树不同,图形可能包含循环,因此我们可能会再次来到同一节点. 2.主要思想: 主要借助一个队列. ...

  8. AspNetCore网关集成Swagger访问使用IdentityServer保护的webapi项目

    创建webapi项目 创建四个webapi项目,两个处理业务,一个网关,一个验证中心.四个项目对应的端口如下, ApiGateway:1999 IdentityServer:16690 Service ...

  9. select ng-change 方法中 拿不到 ng-modal 定义的变量值

    在使用angularjs框架的项目中,select 的数据源有两种绑定方式,在option中使用ng-repeat循环绑定,或者在select中使用ng-option 绑定. 无论哪种绑定方式,均要使 ...

  10. 【洛谷 P5357】 【模板】AC自动机(二次加强版)(AC自动机,差分)

    每次匹配都不停跳fail显然太慢了,于是在每个节点和fail指向的点连一条边,构成一棵树,在这棵树上差分一下就好了. AC自动机 就这个算法而言其实没用想象中那么难. #include <cst ...