设计模式——组合模式

文章目录

  • 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. 组织结构
    • 在企业或组织的结构表示中,员工可以作为叶子对象,部门作为组合对象。可以方便地对组织进行管理和操作,如统计员工数量、计算部门绩效等。
相关推荐
nnsix17 小时前
设计模式 - 建造者模式 笔记
笔记·设计模式·建造者模式
cui178756818 小时前
矩阵拼团 + 复购拼团:新零售最稳的复购模式,规则简单
大数据·人工智能·设计模式·零售
百珏18 小时前
[灰度发布]:全链路透传组件:APM、自研方案与 Java Agent 的实现取舍
后端·设计模式·架构
likerhood20 小时前
设计模式 · 享元模式(Flyweight Pattern)java
java·设计模式·享元模式
AI大法师20 小时前
从 Adobe 焕新看品牌系统升级:Logo、主色、字体与产品体验如何重新对齐
大数据·人工智能·adobe·设计模式
贵慜_Derek21 小时前
《从零实现 Agent 系统》连载 03|控制循环:感知—决策—行动—反思
人工智能·设计模式·架构
nnsix1 天前
设计模式 - 原型模式 笔记
笔记·设计模式·原型模式
nnsix1 天前
设计模式 - 适配器模式 笔记
笔记·设计模式·适配器模式
asdfg12589631 天前
一文理解软件开发中的“设计模式”
java·设计模式·软件开发
云飞云共享云桌面1 天前
SolidWorks 服务器通过云飞云共享云桌面10人研发共享方案
运维·服务器·3d·设计模式·电脑