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

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

树形结构的统一操作接口


1. 模式定义与用途

核心思想

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

​经典场景

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

2. 模式结构解析

UML类图

+---------------------+  
|      Component      |  
+---------------------+  
| + add(c: Component) |  
| + remove(c: Component)|  
| + operation()       |  
+---------------------+  
          ^  
          |  
  +-------+-------+  
  |               |  
+-----------------+ +-----------------+  
|    Composite    | |      Leaf       |  
+-----------------+ +-----------------+  
| - children: list | | + operation()  |  
| + operation()    | +-----------------+  
+-----------------+  

角色说明

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

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

#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. 完整代码:安全模式与扩展功能

场景:图形界面控件树

#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. 缓存遍历结果
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. 并行化渲染
#include <execution>  
void Panel::render() const {  
    std::for_each(std::execution::par, children_.begin(), children_.end(),  
        [](const auto& child) { child->render(); });  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JuicyActiveGilbert

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值