设计模式——组合模式

文章目录

  • 1.定义
  • [2. 结构组成](#2. 结构组成)
  • [3. 组合模式结构](#3. 组合模式结构)
  • [4. 示例代码](#4. 示例代码)
  • [5. 模式优势](#5. 模式优势)
  • [6. 应用场景](#6. 应用场景)

1.定义

组合模式是一种设计模式,它允许将对象组合成树形结构来表示 "部分 - 整体" 的层次关系,使得客户端可以统一地处理单个对象和对象组合,而无需区分它们。

2. 结构组成

  • 组件(Component):
    • 定义:是组合模式中的抽象类或接口,它定义了组合对象和叶子对象的公共操作,为客户端提供统一的操作接口。
    • 常见方法
      • 操作方法:例如execute()或operation()等,在不同的实现类中会有不同的具体操作逻辑。
      • 管理子组件的方法:如add(Component component)用于添加子组件,remove(Component component)用于移除子组件。这些方法在叶子类中可能为空实现。
      • 判断是否为组合对象的方法:如isComposite()用于判断当前对象是否是组合对象。
  • 叶子(Leaf):
    • 定义:是组合模式中的基本对象,它没有子对象,实现了组件接口中定义的方法,完成具体的操作。
    • 特点
      • 叶子对象是树形结构中的叶子节点,是操作的实际执行者。
      • 由于没有子对象,其添加和移除子对象的方法通常是空的。
  • 组合(Composite):
    • 定义:是包含子对象的对象,它可以是其他组合对象或叶子对象。它实现了组件接口中的方法,并且在这些方法中调用其子对象的相应方法。
    • 内部结构:
      • 通常包含一个数据结构(如列表、数组等)来存储子组件,例如children。
      • 实现管理子组件的方法,如add和remove方法会操作内部的子组件存储结构。
      • 在操作方法(如execute)中,会遍历子组件并调用它们的操作方法,实现将操作委派给子组件的功能。
  • 客户端(Client):
    • 作用:是使用组合模式的代码部分,通过组件接口来操作组合对象和叶子对象,无需知道它们的具体类型。
    • 操作方式:
      • 客户端调用组件的操作方法,由具体的对象(叶子或组合)来执行实际的操作。
      • 例如,客户端可以调用component.operation(),而不需要关心component是叶子对象还是组合对象。

3. 组合模式结构

  1. 组件 (Component) 接口描述了树中简单项目和复杂项目所共有的操作。

  2. 叶节点 (Leaf) 是树的基本结构, 它不包含子项目。

    一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。

  3. 容器 (Container)------又名 "组合 (Composite)"------是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。

    容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。

  4. 客户端 (Client) 通过组件接口与所有项目交互。 因此, 客户端能以相同方式与树状结构中的简单或复杂项目交互。

4. 示例代码

cpp 复制代码
#include <iostream>
#include <memory>
#include <list>

using namespace std;

class Component{
protected:
    shared_ptr<Component> parent;
public:
    Component(){ cout<< "Component Construct" <<endl; }
    ~Component(){cout<< "Component Destruct" <<endl; }
    shared_ptr<Component> GetParent(){
        return this->parent;
    }
    
    void SetParent(shared_ptr<Component> parent){
        this->parent = parent;
    }
    
    virtual void Add(shared_ptr<Component>){cout<< "Default Add" <<endl;}
    virtual void Remove(shared_ptr<Component>){cout<< "Default Remove" <<endl;}
    virtual bool IsComposite() const{cout<< "Default IsComposite : false" <<endl; return false;}
    virtual string Operation() const = 0;
};

class Leaf : public Component{
public:
    Leaf(){cout<< "Leaf Construct" <<endl;}
    ~Leaf(){cout<< "Leaf Destruct" <<endl;}
    string Operation()const{
        return "Leaf";
    }       
};

class Composite : public Component,public enable_shared_from_this<Composite>{
protected:
    list<shared_ptr<Component>> children_;
public:
    Composite(){cout<< "Composite Construct" <<endl;}
    ~Composite(){cout<< "Composite Destruct" <<endl;}
    
    void Add(shared_ptr<Component> component){
        children_.push_back(component);
        component->SetParent(shared_from_this());
    }
    
    void Remove(shared_ptr<Component> component){
        children_.remove(component);
        component->SetParent(nullptr);
    }
    
    bool IsComposite() const{return true;}
    
    string Operation() const{
        string result;
        
        for(const shared_ptr<Component> c : children_){
            if(c == children_.back()){
                result += c->Operation();
            }else{
                result += c->Operation() + "+";
            }
        }
        
        return "Branch(" + result + ")";
    }
};

void ClientCode(shared_ptr<Component> component){
    std::cout << "RESULT: " << component->Operation();
}

void ClientCode2(shared_ptr<Component> component1,shared_ptr<Component> component2){
    if (component1->IsComposite()) {
        component1->Add(component2);
    }
    
    cout << "RESULT: " << component1->Operation();
}

int main()
{
    shared_ptr<Component>  simple = make_shared<Leaf>();
    cout << "Client: I've got a simple component:\n";
    ClientCode(simple);
    cout << "\n\n";
    
    shared_ptr<Component> tree = make_shared<Composite>();
    shared_ptr<Component> branch1 = make_shared<Composite>();
    
    shared_ptr<Component> leaf_1 = make_shared<Leaf>();
    shared_ptr<Component> leaf_2 = make_shared<Leaf>();
    shared_ptr<Component> leaf_3 = make_shared<Leaf>();
    branch1->Add(leaf_1);
    branch1->Add(leaf_2);
    
    shared_ptr<Component> branch2 = make_shared<Composite>();
    branch2->Add(leaf_3);
    tree->Add(branch1);
    tree->Add(branch2);
    cout << "Client: Now I've got a composite tree:\n";
    ClientCode(tree);
    std::cout << "\n\n";
    
    cout << "Client: I don't need to check the components classes even when managing the tree:\n";
    ClientCode2(tree, simple);
    cout << "\n";
        
    return 0;
}

5. 模式优势

  1. 简化客户端代码
    • 客户端以统一的方式处理单个对象和组合对象,无需区分它们的具体类型,降低了客户端代码的复杂性。
  2. 易于扩展
    • 可以方便地添加新类型的组件(叶子或组合),只要实现了组件接口,就可以无缝地集成到现有的系统中,符合开闭原则。
  3. 层次结构清晰
    • 非常适合表示具有层次关系的数据结构,例如文件系统(文件和文件夹)、图形系统(简单图形和复合图形)等,使系统的层次结构更加清晰和易于理解。
  4. 代码复用性高
    • 叶子对象和组合对象都基于相同的组件接口,操作方法可以在不同场景下复用。

6. 应用场景

  1. 图形系统
    • 在图形系统中,简单图形(如圆形、矩形)可以作为叶子对象,而由多个图形组成的复杂图形可以作为组合对象。客户端可以统一地对它们进行绘制、移动等操作。
  2. 文件系统
    • 文件系统中的文件可以作为叶子对象,文件夹作为组合对象。操作系统的文件管理器(客户端)可以统一地操作文件和文件夹,如显示内容、复制、删除等操作。
  3. 组织结构
    • 在企业或组织的结构表示中,员工可以作为叶子对象,部门作为组合对象。可以方便地对组织进行管理和操作,如统计员工数量、计算部门绩效等。
相关推荐
渊渟岳5 小时前
掌握设计模式--装饰模式
设计模式
zh路西法7 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
夏旭泽8 小时前
设计模式-备忘录模式
设计模式·备忘录模式
蓝染-惣右介8 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
捕鲸叉12 小时前
C++软件设计模式之类型模式和对象型模式
开发语言·c++·设计模式
诸葛悠闲13 小时前
设计模式——装饰模式
设计模式
西岭千秋雪_13 小时前
设计模式の中介者&发布订阅&备忘录模式
java·观察者模式·设计模式·中介者模式·备忘录模式
捕鲸叉13 小时前
C++软件设计模式之代理(Proxy)模式
c++·设计模式