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
        }
    }

View Code

写了很多备注,大家好好体会一下,里面有两个关键点,仔细把握。

运行结果如下:

三、装饰模式的实现要点

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 = 4096;

    private Stream _stream;

    //……
}

结构很简单,对比结构图看吧。

五、总结

这个模式有点像包饺子,ConcreteComponent其实是饺子馅,Decorator就像饺子皮一样,包什么皮就有什么的样子,皮和皮也可以嵌套,当然我们生活中

的饺子只是包一层皮。其实手机也是一个装饰模式使用的好例子,早期的手机只有接打电话的功能,然后可以发短信和彩信,再后可以拍照了。现在的手机功

能很丰富,其结果也类似装饰的结果。随着社会的进步和技术发展,模块化的手机也出现了,其设计原理越来越接近“装饰模式”。不光是手机,我们身边的很

多家用电器也有类似的发展经历,让我们努力发现生活中的真理吧,然后再在软件环境中慢慢体会。

(0)

相关推荐

  • PHP设计模式之装饰器模式

    PHP设计模式之装饰器模式 工厂模式告一段落,我们来研究其他一些模式.不知道各位大佬有没有尝试过女装?据说女装大佬程序员很多哟.其实,今天的装饰器模式就和化妆这件事很像.相信如果有程序媛MM在的话,马 ...

  • java设计模式之装饰器模式

    装饰器模式的定义: 装饰器模式也叫作包装器模式,指在不改变原有对象的基础上,动态地给一个对象添加一些额外的职责.就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构性设计模式. 装饰器模式提供了比 ...

  • C#设计模式学习笔记:(21)访问者模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8135083.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第九个模式--访 ...

  • 设计模式学习笔记 ———— 简单工厂模式

    # 背景 简单工厂模式是很多程序员学习的第一个设计模式,因为其不但原理简单而且易于上手,在日常工作的代码中也常有体现.今天分享一个基于实现"加"."减".&qu ...

  • 设计模式学习笔记

    由 杨柳依 创建于2019年11月3日,最近更新于2019年11月8日 参考资料:大话设计模式 | 图解设计模式 | 菜鸟教程-设计模式 UML类图 [矩形框]代表一个类(Class).类图分三层: ...

  • Event Bus 设计模式学习笔记

    原文:Design Patterns: Event Bus Motivation 想象一下,有一个包含大量相互交互的组件的大型应用程序,并且您想要一种方法使您的组件进行通信,同时保持松散耦合和关注点分 ...

  • 设计模式-装饰模式学习笔记

    装饰模式(结构型模式) 装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活.[DP] 自己的理解:在不对原有类进行修改的情况下动态的对它进行 ...

  • 设计模式-工厂方法模式学习笔记

    工厂方法模式 定义 工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中.核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负 ...

  • 流畅的python学习笔记(三):把函数视作对象(2:使用一等函数实现设计模式 )

    流畅的python学习笔记(三):把函数视作对象(2:使用一等函数实现设计模式 )

  • 一则公报案例学习笔记:对修改股东出资期限应否适用资本多数决规则的思考|审判研究

    一.问题的提出 2021年第3期<最高人民法院公报案例>刊登了鸿大(上海)投资管理有限公司与姚锦城公司决议纠纷上诉案,裁判要旨为:"公司股东滥用控股地位,以多数决方式通过修改出资 ...

  • JAVA多线程学习笔记整理

    多线程: 三种创建方法 继承Thread类,以线程运行内容重写run方法,创建Thread对象并用start方法启动该线程. (匿名内部类) (Lambda表达式) 实现Runable接口,以线程运行 ...