• 《C++设计模式》——结构型


    前言

    结构模式可以让我们把很多小的东西通过结构模式组合起来成为一个打的结构,但是又不影响各自的独立性,尽可能减少各组件之间的耦合。

    Adapter Class/Object(适配器)

    GOOD:双方都不适合修改的时候,可以考虑使用适配器模式,一般发生在代码的维护阶段。
    在这里插入图片描述
    adapter.h

    #ifndef CLION_TEST_ADAPTER_H
    #define CLION_TEST_ADAPTER_H
    
    #include 
    #include 
    #include 
    
    using namespace std;
    
    // 球员
    class Player {
    protected:
        string name;
    public:
        explicit Player(string strName) { name = std::move(strName); }
    
        virtual void Attack() = 0;
    
        virtual void Defense() = 0;
    };
    
    // 前锋
    class Forwards : public Player {
    public:
        explicit Forwards(string strName) : Player(std::move(strName)) {}
    
        void Attack() final {
            cout << name << "前锋进攻" << endl;
        }
    
        void Defense() final {
            cout << name << "前锋防守" << endl;
        }
    };
    
    // 中锋
    class Center : public Player {
    public:
        explicit Center(string strName) : Player(std::move(strName)) {}
    
        void Attack() final {
            cout << name << "中场进攻" << endl;
        }
    
        void Defense() final {
            cout << name << "中场防守" << endl;
        }
    };
    
    // 后卫
    class Guards : public Player {
    public:
        Guards(string name) : Player(name) {}
    
        void Attack() final {
            cout << name << "后卫进攻" << endl;
        }
    
        void Defense() final {
            cout << name << "后卫防守" << endl;
        }
    };
    
    // Adaptee,此处为外籍中锋,它的接口和Target的接口不一样,需要翻译来帮忙转换
    class ForeignPlayerCenter {
    private:
        string name;
    public:
        void setName(string name) {
            this->name = name;
        }
    
        string getName() const { return name; }
    
        void ForeignAttack() {
            cout << name << "外籍中锋进攻" << endl;
        }
    
        void ForeignDefense() {
            cout << name << "外籍中锋防守" << endl;
        }
    };
    
    
    // 为中场翻译
    class TransLater : public Player {
    private:
        ForeignPlayerCenter *wjzf;
    public:
        explicit TransLater(const string &strName) : Player(strName) {
            wjzf = new ForeignPlayerCenter;
            wjzf->setName(name);
        }
    
        ~TransLater() {
            delete wjzf;
        }
    
        void Attack() {
            wjzf->ForeignAttack();
        }
    
        void Defense() {
            wjzf->ForeignDefense();
        }
    };
    
    #endif //CLION_TEST_ADAPTER_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    main.cpp

    int main()                                
    {                                         
        system("chcp 65001");                 
    
        Player *b = new Forwards("巴蒂尔");      
        b->Attack();                          
        Player *m = new Guards("麦克格雷迪");;     
        m->Attack();                          
                                              
        // 翻译告诉姚明,教练让你既要进攻,又有防守               
        Player* ym = new TransLater("姚明");    
        ym->Attack();                         
        ym->Defense();                        
                                              
        return 0;                             
    }                                         
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Bridge(桥接)

    Composite(组合)

    递归构建树状的组合结构。
    在这里插入图片描述
    需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式。
    应用:自定义控件,也就是把一些基本的控件组合起来,通过编程写成一个定制的控件,比如用两个文本框和一个按钮就可以写一下自定义的登录框控件,实际上,所有的Web控件的基类都是System.Web.UI.Contrrol,而Control基类中就有Add和Remove方法,这就是典型的组合模式的应用。

    Component.h

    #ifndef COMPONENT_H
    #define COMPONENT_H
    
    class Component {
    public:
        Component();
        virtual ~Component();
        virtual void operation() = 0;
        virtual void add(const Component&);
        virtual void remove(const Component&);
        virtual Component* getChild(int);
    };
    
    #endif //COMPONENT_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Component.cpp

    #include "Component.h"
    
    Component::Component() {
    
    }
    
    Component::~Component() {
    
    }
    
    void Component::add(const Component &){
    
    }
    
    void Component::remove(const Component &) {
    
    }
    
    Component *Component::getChild(int) {
        return nullptr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Composite.h

    #ifndef COMPOSITE_H
    #define COMPOSITE_H
    #include "Component.h"
    #include 
    using namespace std;
    
    class Composite: public Component {
    public:
        Composite();
        ~Composite();
        void operation();
        void add(Component* component);
        void remove(Component* component);
        Component* getChild(int idx);
    private:
        vector<Component*> componentVec;
    };
    
    #endif //COMPOSITE_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Composite.cpp

    #include 
    #include "Composite.h"
    
    Composite::Composite() {
    
    }
    
    Composite::~Composite() {
    
    }
    
    void Composite::operation() {
        for (auto &i: componentVec) {
            i->operation();
        }
    }
    
    void Composite::add(Component *component) { componentVec.push_back(component); }
    
    void Composite::remove(
            Component *component) {
        auto it = std::find(componentVec.begin(), componentVec.end(), component);
        if (it != componentVec.end()) {
            componentVec.erase(it);
        }
    
    
    }
    
    Component *Composite::getChild(int idx) {
        return componentVec[idx];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    Leaf.h

    #ifndef LEFT_H
    #define LEFT_H
    #include "Component.h"
    
    class Leaf: public Component {
    public:
        Leaf();
        ~Leaf();
        void operation();
    };
    
    #endif //LEFT_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Leaf.cpp

    #include "Leaf.h"
    #include 
    using namespace std;
    
    
    Leaf::Leaf() {
    
    }
    Leaf::~Leaf() {
    
    }
    void Leaf::operation() {
        cout<<"Leaf operation....."<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    main.cpp

    #include "Leaf.h"
    #include "Component.h"
    #include "Composite.h"
    using namespace std;
    
    int main() {
        Leaf* l = new Leaf();
        l->operation();
        Composite* composite = new Composite();
        composite->add(l);
        composite->operation();
        Component* component = composite->getChild(0);
        component->operation();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component的子部件。
    Leaf在组合中表示叶节点对象,叶节点没有子节点。
    Composite定义有枝节点行为,用于存储子部件,在Component接口中实现与子部件有关的操作,比如增加Add和删除Remove。

    Composite 模式在实现中有一个问题就是要提供对于子节点( Leaf)的管理策略,这里
    使用的是 STL 中的 vector,可以提供其他的实现方式,如数组、链表、 Hash 表等。

    Composite 模式通过和 Decorator 模式有着类似的结构图, 但是 Composite 模式旨在构造类,而 Decorator 模式重在不生成子类即可给对象添加职责。 Decorator 模式重在修饰, 而Composite 模式重在表示。

    Decorator(装饰)

    动态地给一个对象添加一些额外的职责(不重要的功能,只是偶然一次要执行),就增加功能来说,装饰模式比生成子类更为灵活。建造过程不稳定,按正确的顺序串联起来进行控制。
    GOOD:当你向旧的类中添加新代码时,一般是为了添加核心职责或主要行为。而当需要加入的仅仅是一些特定情况下才会执行的特定的功能时(简单点就是不是核心应用的功能),就会增加类的复杂度。装饰模式就是把要添加的附加功能分别放在单独的类中,并让这个类包含它要装饰的对象,当需要执行时,客户端就可以有选择地、按顺序地使用装饰功能包装对象。

    在这里插入图片描述

    decorator.h

    
    #ifndef CLION_TEST_DECORATOR_H
    #define CLION_TEST_DECORATOR_H
    
    #include 
    #include 
    using namespace std;
    
    // 人
    class Person
    {
    private:
        string m_strName;
    public:
        Person(string strName) {
            m_strName = strName;
        }
        Person() {}
        virtual void Show()
        {
            cout<<"装扮的是:"<<m_strName<<endl;
        }
    };
    
    // 装饰类
    class Finery:public Person
    {
    protected:
        Person* m_component;
    public:
        void Decorate(Person* component) {
            m_component = component;
        }
        void Show() override
        {
            m_component->Show();
        }
    };
    
    // T恤
    class TShirts:public Finery
    {
    public:
        void Show() final
        {
            cout<<"T Shirts ";
            m_component->Show();
        }
    };
    
    // 裤子
    class BigTrouer: public Finery
    {
        void Show() final
        {
            cout<<"Big Trouer ";
            m_component->Show();
        }
    };
    
    #endif //CLION_TEST_DECORATOR_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    main.cpp

    #include 
    #include "decorator.h"
    
    using namespace std;
    
    int main() {
        system("chcp 65001");
        // 装饰模式
        Person *p = new Person("小李");
        BigTrouer *bt = new BigTrouer();
        TShirts *ts = new TShirts();
    
        bt->Decorate(p);
        ts->Decorate(bt);
        ts->Show();
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    输出:

    T Shirts Big Trouer 装扮的是:小李
    
    • 1

    调用关系:

    TShirts::Show ==> BigTrouer::Show ==> Person::Show
    
    • 1

    Facade(外观)

    GOOD:为子系统的一组接口提供一个一致的界面。使用户使用起来更加方便。
    在这里插入图片描述
    facade.h

    #ifndef CLION_TEST_FACADE_H
    #define CLION_TEST_FACADE_H
    
    #include 
    
    using namespace std;
    
    
    class Stock1 {
    public:
        void Buy() {
            cout << "股票1买入" << endl;
        }
    
        void Sell() {
            cout << "股票1卖出" << endl;
        }
    };
    
    class Stock2 {
    public:
        void Buy() {
            cout << "股票2买入" << endl;
        }
    
        void Sell() {
            cout << "股票2卖出" << endl;
        }
    };
    
    class Stock3 {
    public:
        void Buy() {
            cout << "股票3买入" << endl;
        }
    
        void Sell() {
            cout << "股票3卖出" << endl;
        }
    };
    
    // 基金类
    class Fund {
    private:
        Stock1 *gu1;
        Stock2 *gu2;
        Stock3 *gu3;
    public:
        Fund() {
            gu1 = new Stock1();
            gu2 = new Stock2();
            gu3 = new Stock3();
        }
    
        void BuyFund() {
            gu1->Buy();
            gu2->Buy();
            gu3->Buy();
        }
    
        void SellFund() {
            gu1->Sell();
            gu2->Sell();
            gu3->Sell();
        }
    
    };
    
    #endif //CLION_TEST_FACADE_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    main.cpp

    #include "facade.h"
    
    using namespace std;
    
    int main() {
        system("chcp 65001");
        // 外观模式
        Fund* jijin = new Fund();
        // 基金购买
        jijin->BuyFund();
        // 基金赎回
        jijin->SellFund();
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用外观模式的三个场景:
    1、在设计初期阶段,应该要有意识的将不同的两个层分离,层与层之间建立外观Facade。
    2、在开发阶段。子系统往往因为不断的重构演化而变得越来越复杂,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。
    3、在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,为新系统开发一个外观Facade类,来提供涉及粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

    Flyweight(享元)

    Proxy(代理)

    GOOD:远程代理,可以隐藏一个对象在不同地址空间的事实
    虚拟代理:通过代理来存放需要很长时间实例化的对象
    安全代理:用来控制真实对象的访问权限
    智能引用:当调用真实对象时,代理处理另外一些事
    在这里插入图片描述

    proxy.h

    #ifndef CLION_TEST_PROXY_H
    #define CLION_TEST_PROXY_H
    
    #include 
    #include 
    
    using namespace std;
    
    // 定义接口
    class Interface {
    public:
        virtual void Request() = 0;
    };
    
    // 真实类
    class RealClass : public Interface {
    public:
        void Request() final {
            cout << "真实的请求" << endl;
        }
    };
    
    // 代理类
    class ProxyClass : public Interface {
    private:
        RealClass *m_realClass;
    public:
        void Request() final {
            if (m_realClass == nullptr) {
                m_realClass = new RealClass();
            }
            m_realClass->Request();
        }
    };
    
    #endif //CLION_TEST_PROXY_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    main.cpp

    int main() {
        ProxyClass* test = new ProxyClass();
        test->Request();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    调用关系:

    ProxyClass::Request ==> RealClass::Request 
    
    • 1

    后记

  • 相关阅读:
    支持在代码编辑器中调试接口,IDEA插件推荐
    02【SpringMVC的工作流程】
    JAVA毕业设计146—基于Java+Springboot+vue+uniapp的景区旅游购票小程序(源代码+数据库+9000字论文)
    SQL查询中的小技巧:SELECT 1 和 LIMIT 1 替代 count(*)
    elasticsearch-7.8.0 集群搭建
    Docker 安装 mysql5.7
    力扣 136. 只出现一次的数字
    Pandas连接MySQL数据库
    【Ingress】
    Windows10安装配置Docker客户端和WSL2与Hyper-V虚拟机
  • 原文地址:https://blog.csdn.net/XZ2585458279/article/details/132738584