zoukankan      html  css  js  c++  java
  • 【设计模式

    在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等,这些都可以使用装饰模式。它的主要作用就是:

    • 增强一个类原有的功能。
    • 为一个类添加新的功能。

    并且装饰模式也不会改变原有的类

    在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰模式来实现。


    一、定义与特点

    装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。

    主要优点有:

    • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用;
    • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果;
    • 装饰器模式完全遵守开闭原则。

    其主要缺点是:装饰模式会增加许多子类,过度使用会增加程序得复杂性。


    二、结构与实现

    通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。

    如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,就可以在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式。下面来分析其基本结构和实现方法。

    装饰模式的结构

    装饰模式主要包含以下角色:

    1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
    2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
    3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
    4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

    装饰模式的结构图如下图所示:
    装饰模式的结构图


    装饰模式的实现代码如下:

    #include <iostream>
    
    using namespace std;
    
    // 抽象构件角色
    class Component {
    public:
        Component() {}
        virtual void operation() {}
    };
    
    // 具体构件角色
    class ConcreteComponent: public Component {
    public:
        ConcreteComponent() {
            cout << "创建具体构件角色" << endl;
        }
        void operation() {
            cout << "调用具体构件角色的方法operation()" << endl;
        }
    };
    
    // 抽象装饰角色
    class Decorator: public Component {
    public:
        Decorator(Component *component) {
            this->component = component;
        }
        void operation() {
            component->operation();
        }
    
    public:
        Component *component;
    };
    
    // 具体装饰角色
    class ConcreteDecorator: public Decorator {
    public:
        // 关键要加上后面的Decorator(component)
        ConcreteDecorator(Component *component):Decorator(component) { }
        void operation() {
            component->operation();
            addedFunction();
        }
        void addedFunction() {
            cout << "为具体构件角色增加额外的功能addedFunction()" << endl;
        }
    };
    
    int main()
    {
        Component *com = new ConcreteComponent();
        com->operation();
        cout << "---------------------------------" << endl;
        Component *dec = new ConcreteDecorator(com);
        dec->operation();
    
        return 0;
    }
    

    程序运行结果如下:

    创建具体构件角色
    调用具体构件角色的方法operation()
    ---------------------------------
    调用具体构件角色的方法operation()
    为具体构件角色增加额外的功能addedFunction()
    

    可以看到,ConcreteDecorator 装饰给 ConcreteComponent 类添加了动作 addedFunction。


    三、应用实例

    3.1 用于增强功能的装饰模式

    我们用程序来模拟一下戴上装饰品提高我们颜值的过程:

    #include <iostream>
    
    using namespace std;
    
    // 抽象构件角色:颜值接口
    class IBeauty {
    public:
        IBeauty() {}
        virtual int getBeautyValue() {}
    };
    
    // 具体构件角色:Me类
    class Me: public IBeauty {
    public:
        // @Override
        int getBeautyValue() {
            return 100;
        }
    };
    
    // 具体装饰角色:戒指装饰类
    class RingDecorator: public IBeauty {
    public:
        RingDecorator(IBeauty *me) {
            this->me = me;
        }
    
        // @Override
        int getBeautyValue() {
            return me->getBeautyValue() + 20;
        }
    
    public:
        IBeauty *me;
    };
    
    int main()
    {
        IBeauty *me = new Me();
        cout << "我原本的颜值:" << me->getBeautyValue() << endl;
    
        IBeauty *meWithRing = new RingDecorator(me);
        cout << "戴上了戒指后,我的颜值:" << meWithRing->getBeautyValue() << endl;
    
        return 0;
    }
    

    运行程序,输出如下:

    我原本的颜值:100
    戴上了戒指后,我的颜值:120
    

    这就是最简单的增强功能的装饰模式。以后我们可以添加更多的装饰类,比如:

    // 具体装饰角色:耳环装饰类
    class EarringDecorator: public IBeauty {
    public:
        EarringDecorator(IBeauty *me) {
            this->me = me;
        }
    
        // @Override
        int getBeautyValue() {
            return me->getBeautyValue() + 50;
        }
    
    private:
        IBeauty *me;
    };
    
    // 具体装饰角色:项链装饰类
    class NecklaceDecorator: public IBeauty {
    public:
        NecklaceDecorator(IBeauty *me) {
            this->me = me;
        }
    
        // @Override
        int getBeautyValue() {
            return me->getBeautyValue() + 50;
        }
    
    private:
        IBeauty *me;
    };
    
    int main()
    {
        IBeauty *me = new Me();
        cout << "我原本的颜值:" << me->getBeautyValue() << endl;
    
        // 随意挑选装饰
        IBeauty *meWithRing = new RingDecorator(me);
        cout << "戴上了戒指后,我的颜值:" << meWithRing->getBeautyValue() << endl;
    
        // 多次装饰
        IBeauty *meWithManyDecorators = new NecklaceDecorator(new RingDecorator(new EarringDecorator(me)));
        cout << "戴上耳环、戒指、项链后,我的颜值:" << meWithManyDecorators->getBeautyValue() << endl;
    
        // 任意搭配装饰
        IBeauty *meWithNecklaceAndRing = new NecklaceDecorator(new RingDecorator(me));
        cout << "戴上戒指、项链后,我的颜值:" << meWithNecklaceAndRing->getBeautyValue() << endl;
    
        return 0;
    }
    

    运行程序,输出如下:

    我原本的颜值:100
    戴上了戒指后,我的颜值:120
    戴上耳环、戒指、项链后,我的颜值:220
    戴上戒指、项链后,我的颜值:200
    

    可以看到,装饰器也实现了 IBeauty 接口,并且没有添加新的方法,也就是说这里的装饰器仅用于增强功能并不会改变 Me 原有的功能,这种装饰模式称之为透明装饰模式,由于没有改变接口,也没有新增方法,所以透明装饰模式可以无限装饰

    装饰模式是继承的一种替代方案。本例如果不使用装饰模式,而是改用继承实现的话,戴着戒指的 Me 需要派生一个子类、戴着项链的 Me 需要派生一个子类、戴着耳环的 Me 需要派生一个子类、戴着戒指 + 项链的需要派生一个子类......各种各样的排列组合会造成类爆炸。而采用了装饰模式就只需要为每个装饰品生成一个装饰类即可,所以说就增加对象功能来说,装饰模式比生成子类实现更为灵活


    3.2 用于添加功能的装饰模式

    我们用程序来模拟一下房屋装饰粘钩后,新增了挂东西功能的过程:

    #include <iostream>
    
    using namespace std;
    
    // 抽象构件角色:颜值接口
    class IHouse {
    public:
        virtual void live() {}
    };
    
    // 具体构件角色:房屋类
    class House: public IHouse {
    public:
        // @Override
        void live() {
            cout << "房屋原有的功能:居住功能" << endl;
        }
    };
    
    // 抽象装饰角色:粘钩装饰器接口
    class IStickyHookHouse: public IHouse {
    public:
        virtual void hangThings() {}
    };
    
    // 具体装饰角色:粘钩装饰类
    class StickyHookDecorator: public IStickyHookHouse {
    public:
        StickyHookDecorator(IHouse *house) {
            this->house = house;
        }
    
        // @Override
        void live() {
            house->live();
        }
    
        // @Override
        void hangThings() {
            cout << "有了粘钩后,新增了挂东西功能" << endl;
        }
    
    private:
        IHouse *house;
    };
    
    int main()
    {
        IHouse *house = new House();
        house->live();
    
        // 新增功能
        IStickyHookHouse *stickyHookHouse = new StickyHookDecorator(house);
        stickyHookHouse->live();
        stickyHookHouse->hangThings();
    
        return 0;
    }
    

    运行程序,显示如下:

    房屋原有的功能:居住功能
    房屋原有的功能:居住功能
    有了粘钩后,新增了挂东西功能
    

    这就是用于新增功能的装饰模式。我们在接口中新增了方法:hangThings,然后在装饰器中将 House 类包装起来,之前 House 中的方法仍然调用 house 去执行,也就是说我们并没有修改原有的功能,只是扩展了新的功能,这种模式在装饰模式中称之为半透明装饰模式

    为什么叫半透明呢?由于新的接口 IStickyHookHouse 拥有之前 IHouse 不具有的方法,所以我们如果要使用装饰器中添加的功能,就不得不区别对待装饰前的对象和装饰后的对象。也就是说客户端要使用新方法,必须知道具体的装饰类 StickyHookDecorator,所以这个装饰类对客户端来说是可见的、不透明的。而被装饰者不一定要是 House,它可以是实现了 IHouse 接口的任意对象,所以被装饰者对客户端是不可见的、透明的。由于一半透明,一半不透明,所以称之为半透明装饰模式。

    我们可以添加更多的装饰器:

    // 抽象装饰角色:镜子装饰器的接口
    class IMirrorHouse: public IHouse {
    public:
        virtual void lookMirror() {}
    };
    
    // 具体装饰角色:镜子类
    class MirrorDecorator: public IMirrorHouse {
    public:
        MirrorDecorator(IHouse *house) {
            this->house = house;
        }
    
        // @Override
        void live() {
            house->live();
        }
    
        // @Override
        void lookMirror() {
            cout << "有了镜子后,新增了照镜子功能" << endl;
        }
    
    private:
        IHouse *house;
    };
    
    int main()
    {
        IHouse *house = new House();
        house->live();
    
        IMirrorHouse *mirrorHouse = new MirrorDecorator(house);
        mirrorHouse->live();
        mirrorHouse->lookMirror();
    
        return 0;
    }
    

    运行程序,输出如下:

    房屋原有的功能:居住功能
    房屋原有的功能:居住功能
    有了镜子后,新增了照镜子功能
    

    现在我们仿照透明装饰模式的写法,同时添加粘钩和镜子装饰试一试:

    int main()
    {
        IHouse *house = new House();
        house->live();
    
        IStickyHookHouse *stickyHookHouse = new StickyHookDecorator(house);
        IMirrorHouse *houseWithStickyHookMirror = new MirrorDecorator(stickyHookHouse);
        houseWithStickyHookMirror->live();
        houseWithStickyHookMirror->hangThings(); // 这里会报错,找不到 hangThings 方法
        houseWithStickyHookMirror->lookMirror();
    
        return 0;
    }
    

    我们会发现,第二次装饰时,无法获得上一次装饰添加的方法。原因很明显,当我们用 IMirrorHouse 装饰器后,接口变为了 IMirrorHouse,这个接口中并没有 hangThings 方法。

    那么我们能否让 IMirrorHouse 继承自 IStickyHookHouse,以实现新增两个功能呢?

    可以,但那样做的话两个装饰类之间有了依赖关系,那就不是装饰模式了。装饰类不应该存在依赖关系,而应该在原本的类上进行装饰。这就意味着,半透明装饰模式中我们无法多次装饰

    只要添加了新功能的装饰模式都称之为 半透明装饰模式,他们都具有不可以多次装饰的特点。仔细理解上文半透明名称的由来就知道了,“透明”指的是我们无需知道被装饰者具体的类,既增强了功能,又添加了新功能的装饰模式仍然具有半透明特性。


    参考:

    知乎 - 如何学习设计模式? 热门回答

    装饰模式(装饰设计模式)详解

    菜鸟教程 - 设计模式篇

    C++装饰模式


  • 相关阅读:
    测试签名和验证签名
    自定义mssql的CLR函数
    关于C#的Process的内存相关属性解读
    测试C#发送邮件
    关于wmv视频格式
    练习命名管道的使用
    web中局部滚动条
    C#修改文件的安全属性时报“没有可以设置的标志”
    c#的FileSystemWatcher对象监视文件的变化的事件,无休止的触发事件的解决办法
    为什么要给自己设限?
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/13679907.html
Copyright © 2011-2022 走看看