【C++设计模式】第八篇:组合模式(Composite)

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

树形结构的统一操作接口


1. 模式定义与用途

核心思想

  • 组合模式:将对象组合成树形结构以表示"部分-整体"层次结构,使得客户端可以统一处理单个对象和组合对象。
  • 关键用途
    1.简化递归结构操作(如遍历目录树、渲染UI控件树)。
    2.隐藏复杂结构的差异,提供一致性接口。

​经典场景

  1. 文件系统管理(文件与文件夹的统一操作)。
  2. 图形界面容器(窗口包含按钮、面板等子控件)。

2. 模式结构解析

UML类图

plaintext 复制代码
+---------------------+  
|      Component      |  
+---------------------+  
| + add(c: Component) |  
| + remove(c: Component)|  
| + operation()       |  
+---------------------+  
          ^  
          |  
  +-------+-------+  
  |               |  
+-----------------+ +-----------------+  
|    Composite    | |      Leaf       |  
+-----------------+ +-----------------+  
| - children: list | | + operation()  |  
| + operation()    | +-----------------+  
+-----------------+  

角色说明

  1. Component:抽象接口,定义叶子和容器的共同操作(如add、remove)。
  2. Leaf:叶子节点,无子组件(如文件)。
  3. Composite:容器节点,存储子组件并实现递归逻辑(如文件夹)。

3. 简单示例:透明模式(统一接口)​

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

// 抽象组件(透明模式:叶子与容器接口一致)  
class FileSystemComponent {  
public:  
    virtual ~FileSystemComponent() = default;  
    virtual void add(std::shared_ptr<FileSystemComponent> item) {  
        throw std::runtime_error("叶子节点不支持此操作");  
    }  
    virtual void list() const = 0;  
};  

// 叶子:文件  
class File : public FileSystemComponent {  
public:  
    File(const std::string& name) : name_(name) {}  

    void list() const override {  
        std::cout << "文件: " << name_ << "\n";  
    }  

private:  
    std::string name_;  
};  

// 容器:文件夹  
class Folder : public FileSystemComponent {  
public:  
    Folder(const std::string& name) : name_(name) {}  

    void add(std::shared_ptr<FileSystemComponent> item) override {  
        children_.push_back(item);  
    }  

    void list() const override {  
        std::cout << "文件夹: " << name_ << "\n";  
        for (const auto& child : children_) {  
            child->list();  
        }  
    }  

private:  
    std::string name_;  
    std::vector<std::shared_ptr<FileSystemComponent>> children_;  
};  

// 使用示例  
int main() {  
    auto root = std::make_shared<Folder>("根目录");  
    auto docs = std::make_shared<Folder>("文档");  
    auto file1 = std::make_shared<File>("简历.pdf");  
    auto file2 = std::make_shared<File>("笔记.txt");  

    docs->add(file1);  
    docs->add(file2);  
    root->add(docs);  

    root->list();  // 递归列出所有内容  
}  

4. 完整代码:安全模式与扩展功能

场景:图形界面控件树

cpp 复制代码
#include <iostream>  
#include <vector>  
#include <memory>  
#include <algorithm>  

// 抽象组件(安全模式:叶子与容器接口分离)  
class UIComponent {  
public:  
    virtual ~UIComponent() = default;  
    virtual void render() const = 0;  
    virtual std::string getName() const = 0;  
};  

// 容器接口(仅容器支持添加/删除)  
class UIContainer : public UIComponent {  
public:  
    virtual void add(std::shared_ptr<UIComponent> child) = 0;  
    virtual void remove(std::shared_ptr<UIComponent> child) = 0;  
};  

// 叶子:按钮  
class Button : public UIComponent {  
public:  
    Button(const std::string& name) : name_(name) {}  

    void render() const override {  
        std::cout << "渲染按钮: " << name_ << "\n";  
    }  

    std::string getName() const override {  
        return name_;  
    }  

private:  
    std::string name_;  
};  

// 容器:面板  
class Panel : public UIContainer {  
public:  
    Panel(const std::string& name) : name_(name) {}  

    void add(std::shared_ptr<UIComponent> child) override {  
        children_.push_back(child);  
    }  

    void remove(std::shared_ptr<UIComponent> child) override {  
        auto it = std::find(children_.begin(), children_.end(), child);  
        if (it != children_.end()) {  
            children_.erase(it);  
        }  
    }  

    void render() const override {  
        std::cout << "渲染面板: " << name_ << "\n";  
        for (const auto& child : children_) {  
            child->render();  
        }  
    }  

    std::string getName() const override {  
        return name_;  
    }  

private:  
    std::string name_;  
    std::vector<std::shared_ptr<UIComponent>> children_;  
};  

// 客户端代码  
int main() {  
    auto mainPanel = std::make_shared<Panel>("主面板");  
    auto button1 = std::make_shared<Button>("确定");  
    auto button2 = std::make_shared<Button>("取消");  
    auto subPanel = std::make_shared<Panel>("子面板");  
    auto button3 = std::make_shared<Button>("更多");  

    subPanel->add(button3);  
    mainPanel->add(button1);  
    mainPanel->add(button2);  
    mainPanel->add(subPanel);  

    mainPanel->render();  
    // 输出:  
    // 渲染面板: 主面板  
    // 渲染按钮: 确定  
    // 渲染按钮: 取消  
    // 渲染面板: 子面板  
    // 渲染按钮: 更多  
}  

5. 优缺点分析

优点 ​​缺点
统一处理简单与复杂对象 透明模式违反接口隔离原则(叶子支持add)
支持递归操作与树形遍历 容器需管理子节点生命周期,增加复杂度
灵活扩展新组件类型 对性能敏感场景不友好(深层次遍历)

6. 调试与优化策略

调试技巧(VS2022)​

  1. 递归调用跟踪
    在render()方法内设置条件断点(如getName() == "子面板")。
  2. 内存泄漏检测
    使用VS2022内置诊断工具(Debug > Windows > Memory Usage)。

性能优化

  1. 缓存遍历结果
cpp 复制代码
class CachedPanel : public Panel {  
public:  
    void render() const override {  
        if (!cached_) {  
            cachedOutput_ = generateRenderOutput();  
            cached_ = true;  
        }  
        std::cout << cachedOutput_;  
    }  
private:  
    mutable bool cached_ = false;  
    mutable std::string cachedOutput_;  
};  
  1. 并行化渲染
cpp 复制代码
#include <execution>  
void Panel::render() const {  
    std::for_each(std::execution::par, children_.begin(), children_.end(),  
        [](const auto& child) { child->render(); });  
}  
相关推荐
王燕龙(大卫)11 分钟前
递归下降算法
开发语言·c++·算法
郭涤生31 分钟前
C++ 完美转发
c++·算法
whoarethenext1 小时前
数据结构堆的c/c++的实现
c语言·数据结构·c++·
2401_858286111 小时前
CD36.【C++ Dev】STL库的string的使用 (下)
开发语言·c++·类和对象·string
強云1 小时前
性能优化-初识(C++)
c++·性能优化
虾球xz3 小时前
游戏引擎学习第263天:添加调试帧滑块
c++·学习·游戏引擎
努力努力再努力wz3 小时前
【c++深入系列】:万字详解vector(附模拟实现的vector源码)
运维·开发语言·c++·c
yxc_inspire4 小时前
基于Qt的app开发第六天
开发语言·c++·qt
ouliten4 小时前
《C++ Templates》:有关const、引用、指针的一些函数模板实参推导的例子
c++
李匠20244 小时前
C++GO语言微服务项目之 go语言基础语法
开发语言·c++·后端·golang