• C++设计模式之——组合模式



    组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组织成树形结构,并且能够像处理单个对象一样处理整个组合结构。在组合模式中,组合对象与单个对象(也称为叶子对象或简单对象)有着相同的接口,所以客户端可以一致地对待它们,无需知道处理的是单个对象还是整个组合结构。

    组合模式的基本概念:**

    1. Component(抽象构件):定义了所有对象所共有的接口,包括组合对象和叶子对象。声明了所有用来管理子组件的方法,比如添加、删除和获取子组件等,但在组合对象中实现这些方法,而在叶子对象中则可能提供默认或空实现。

    2. Leaf(叶子节点):是组合结构中的基本元素,没有子节点,实现了Component接口。

    3. Composite(组合节点):包含了多个Component对象,可以用来存储和管理它的子部件,同时自身也是Component接口的实现。组合节点提供了用于管理子节点的方法实现。

    C++代码案例简述:

    下面是一个简单的C++代码示例,展示了如何实现组合模式:

    // 抽象构件 Component
    class Component {
    public:
        virtual ~Component() {}
        virtual void operation() = 0; // 假设这是所有组件都有的通用操作
        virtual void add(Component* child) {} // 在组合节点中实现,在叶子节点中可能是空操作
        virtual void remove(Component* child) {} // 同上
        virtual Component* getChild(int index) { return nullptr; } // 返回指定索引的子组件,叶子节点返回nullptr
    };
    
    // 叶子节点 Leaf
    class Leaf : public Component {
    public:
        void operation() override {
            // 叶子节点的具体实现
        }
    };
    
    // 组合节点 Composite
    class Composite : public Component {
    private:
        vector<Component*> children; // 存储子组件的容器
    
    public:
        void add(Component* child) override {
            children.push_back(child);
        }
    
        void remove(Component* child) override {
            auto it = find(children.begin(), children.end(), child);
            if (it != children.end()) {
                children.erase(it);
            }
        }
    
        Component* getChild(int index) override {
            if (index >= 0 && index < children.size()) {
                return children[index];
            }
            return nullptr;
        }
    
        void operation() override {
            for (auto& child : children) {
                child->operation(); // 递归调用子组件的操作
            }
            // 组合节点自己的具体实现
        }
    };
    
    // 使用示例
    int main() {
        Leaf* leaf1 = new Leaf();
        Leaf* leaf2 = new Leaf();
        Composite* composite = new Composite();
    
        composite->add(leaf1);
        composite->add(leaf2);
    
        composite->operation(); // 会同时调用两个叶子节点以及自身的方法
    
        delete leaf1;
        delete leaf2;
        delete composite;
    
        return 0;
    }
    
    • 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

    在这个例子中,Component抽象基类LeafComposite都是其派生类。Composite内部维护了一个容器来保存子Component对象,并在operation()方法中通过递归来调用每个子组件的operation()方法。这样,无论是单独调用叶子节点的操作,还是调用组合节点的操作,客户端代码都可以保持一致性。

    在实际项目中,为了更好地管理和资源释放,建议使用智能指针来代替原始指针,尤其是在C++11之后,可以有效避免内存泄漏和悬挂指针的问题。

    当然,我们可以进一步完善上述代码示例,引入智能指针以提高内存管理的安全性。以下是如何使用C++11的std::shared_ptr来重构组合模式的例子:

    #include 
    #include 
    
    // 抽象构件 Component
    class Component {
    public:
        virtual ~Component() {}
        virtual void operation() = 0;
        virtual void add(std::shared_ptr<Component> child) = 0;
        virtual void remove(std::shared_ptr<Component> child) = 0;
        virtual std::shared_ptr<Component> getChild(int index) = 0;
    };
    
    // 叶子节点 Leaf
    class Leaf : public Component {
    public:
        void operation() override {
            // 叶子节点的具体实现
        }
    
        // 对于叶子节点,下述方法为空实现
        void add(std::shared_ptr<Component> child) override {}
        void remove(std::shared_ptr<Component> child) override {}
        std::shared_ptr<Component> getChild(int index) override {
            return nullptr;
        }
    };
    
    // 组合节点 Composite
    class Composite : public Component {
    private:
        std::vector<std::shared_ptr<Component>> children;
    
    public:
        void add(std::shared_ptr<Component> child) override {
            children.push_back(child);
        }
    
        void remove(std::shared_ptr<Component> child) override {
            auto it = std::find(children.begin(), children.end(), child);
            if (it != children.end()) {
                children.erase(it);
            }
        }
    
        std::shared_ptr<Component> getChild(int index) override {
            if (index >= 0 && index < children.size()) {
                return children[index];
            }
            return nullptr;
        }
    
        void operation() override {
            for (const auto& child : children) {
                child->operation(); // 递归调用子组件的操作
            }
            // 组合节点自己的具体实现
        }
    };
    
    // 使用示例
    int main() {
        auto leaf1 = std::make_shared<Leaf>();
        auto leaf2 = std::make_shared<Leaf>();
        auto composite = std::make_shared<Composite>();
    
        composite->add(leaf1);
        composite->add(leaf2);
    
        composite->operation(); // 会同时调用两个叶子节点以及自身的方法
    
        return 0;
    }
    
    • 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

    通过使用std::shared_ptr,我们不再需要手动进行内存管理,当组合对象或叶子对象不再被引用时,它们占用的内存会自动释放。这样不仅提高了代码的健壮性和可维护性,还降低了出现内存泄露的风险。
    尽管以上代码已经实现了组合模式的核心思想,但在实际应用中,我们可能还需要考虑其他因素,例如访问子组件的方式可能不仅仅是按索引获取,还可以支持更灵活的方式来遍历所有子组件。下面是对上述代码进行扩展,加入迭代器以便更方便地遍历子组件:

    #include 
    #include 
    #include  // 引入迭代器功能
    
    // 抽象构件 Component
    class Component {
    public:
        virtual ~Component() {}
        virtual void operation() = 0;
        virtual void add(std::shared_ptr<Component> child) = 0;
        virtual void remove(std::shared_ptr<Component> child) = 0;
    
        // 提供迭代器接口,使得可以通过范围for循环遍历子组件
        virtual std::vector<std::shared_ptr<Component>>::const_iterator begin() const = 0;
        virtual std::vector<std::shared_ptr<Component>>::const_iterator end() const = 0;
    };
    
    // 叶子节点 Leaf
    class Leaf : public Component {
    public:
        void operation() override {
            // 叶子节点的具体实现
        }
    
        // 对于叶子节点,下述方法为空实现
        void add(std::shared_ptr<Component> child) override {}
        void remove(std::shared_ptr<Component> child) override {}
        
        std::vector<std::shared_ptr<Component>>::const_iterator begin() const override {
            static std::shared_ptr<Component> emptyList[] = {};
            return emptyList;
        }
    
        std::vector<std::shared_ptr<Component>>::const_iterator end() const override {
            static std::shared_ptr<Component> emptyList[] = {};
            return emptyList + 0;
        }
    };
    
    // 组合节点 Composite
    class Composite : public Component {
    private:
        std::vector<std::shared_ptr<Component>> children;
    
    public:
        void add(std::shared_ptr<Component> child) override {
            children.push_back(child);
        }
    
        void remove(std::shared_ptr<Component> child) override {
            auto it = std::find(children.begin(), children.end(), child);
            if (it != children.end()) {
                children.erase(it);
            }
        }
    
        std::shared_ptr<Component> getChild(int index) override {
            if (index >= 0 && index < children.size()) {
                return children[index];
            }
            return nullptr;
        }
    
        std::vector<std::shared_ptr<Component>>::const_iterator begin() const override {
            return children.begin();
        }
    
        std::vector<std::shared_ptr<Component>>::const_iterator end() const override {
            return children.end();
        }
    
        void operation() override {
            for (const auto& child : children) {
                child->operation(); // 递归调用子组件的操作
            }
            // 组合节点自己的具体实现
        }
    };
    
    // 使用示例,现在可以使用范围for循环遍历子组件
    int main() {
        auto leaf1 = std::make_shared<Leaf>();
        auto leaf2 = std::make_shared<Leaf>();
        auto composite = std::make_shared<Composite>();
    
        composite->add(leaf1);
        composite->add(leaf2);
    
        // 遍历并调用所有子组件的操作
        for (const auto& child : *composite) {
            child->operation();
        }
    
        return 0;
    }
    
    • 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

    通过添加迭代器接口,现在可以直接使用范围for循环遍历组合对象的所有子组件,这使得代码更加简洁易读,同时也符合STL容器和算法的设计风格。
    另外,对于组合模式的应用场景,通常还包括动态构建和修改组合结构的需求。以下是一个更完整的示例,展示如何在运行时创建和调整组合结构,并演示如何利用组合模式简化客户端代码:

    #include 
    #include 
    #include 
    #include 
    
    // 抽象构件 Component
    class Component {
    public:
        virtual ~Component() {}
        virtual void operation() const = 0;
        virtual void add(std::shared_ptr<Component> child) = 0;
        virtual void remove(std::shared_ptr<Component> child) = 0;
        virtual std::vector<std::shared_ptr<Component>>::const_iterator begin() const = 0;
        virtual std::vector<std::shared_ptr<Component>>::const_iterator end() const = 0;
    };
    
    // 叶子节点 Leaf
    class Leaf : public Component {
    public:
        explicit Leaf(const std::string& name) : name(name) {}
    
        void operation() const override {
            std::cout << "Leaf: " << name << std::endl;
        }
    
        // 对于叶子节点,下述方法为空实现
        void add(std::shared_ptr<Component> child) override {}
        void remove(std::shared_ptr<Component> child) override {}
    
        std::vector<std::shared_ptr<Component>>::const_iterator begin() const override {
            static std::shared_ptr<Component> emptyList[] = {};
            return emptyList;
        }
    
        std::vector<std::shared_ptr<Component>>::const_iterator end() const override {
            static std::shared_ptr<Component> emptyList[] = {};
            return emptyList + 0;
        }
    
    private:
        std::string name;
    };
    
    // 组合节点 Composite
    class Composite : public Component {
    public:
        void operation() const override {
            std::cout << "Composite: " << std::endl;
            for (const auto& child : children) {
                child->operation();
            }
        }
    
        void add(std::shared_ptr<Component> child) override {
            children.push_back(child);
        }
    
        void remove(std::shared_ptr<Component> child) override {
            auto it = std::find(children.begin(), children.end(), child);
            if (it != children.end()) {
                children.erase(it);
            }
        }
    
        std::vector<std::shared_ptr<Component>>::const_iterator begin() const override {
            return children.begin();
        }
    
        std::vector<std::shared_ptr<Component>>::const_iterator end() const override {
            return children.end();
        }
    
    private:
        std::vector<std::shared_ptr<Component>> children;
    };
    
    // 客户端代码
    int main() {
        auto leaf1 = std::make_shared<Leaf>("Leaf1");
        auto leaf2 = std::make_shared<Leaf>("Leaf2");
        auto composite1 = std::make_shared<Composite>();
        auto composite2 = std::make_shared<Composite>();
    
        composite1->add(leaf1);
        composite1->add(composite2);
        composite2->add(leaf2);
    
        // 调用组合结构的操作
        composite1->operation();
    
        // 动态修改组合结构
        composite1->remove(leaf1);
        composite2->add(leaf1);
    
        composite1->operation(); // 结构改变后重新调用操作
    
        return 0;
    }
    
    • 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

    这个示例中,我们创建了一个包含两个叶子节点和一个嵌套组合节点的组合结构,并能够在运行时根据需求动态添加、删除组件。客户端代码只需直接调用组合结构的operation()方法,而无需关心组合的具体结构和内部细节。

    python推荐学习汇总连接:
    50个开发必备的Python经典脚本(1-10)

    50个开发必备的Python经典脚本(11-20)

    50个开发必备的Python经典脚本(21-30)

    50个开发必备的Python经典脚本(31-40)

    50个开发必备的Python经典脚本(41-50)
    ————————————————

    ​最后我们放松一下眼睛
    在这里插入图片描述

  • 相关阅读:
    机器学习 - 决策树:技术全解与案例实战
    虹科示波器 | 汽车免拆检测 | 2017款长安福特翼虎车发动机故障灯异常点亮
    如何使用SQL系列 之 如何在SQL中使用主键
    设计模式---责任链模式
    TypeScript配置文件设置详解
    Rust个人学习笔记2
    Linux三剑客:awk的高级用法
    10_6 input输入子系统,流程解析
    身份证号码算法解析与Java代码实现
    03 | 基础篇:经常说的 CPU 上下文切换是什么意思?(上)笔记
  • 原文地址:https://blog.csdn.net/qqrrjj2011/article/details/136362860