目录

【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(); });  
}  
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
杨某一辰15 分钟前
库magnet使用指南
c++·多线程·
ricky_fan2 小时前
Leetcode39:组合总和——回溯算法
开发语言·c++·leetcode
爱看烟花的码农2 小时前
LeetCode 热题 8/100打卡
c++·python·算法·leetcode
Ye-Maolin2 小时前
23种GoF设计模式
设计模式
共享家95272 小时前
C/C++内存管理:从基础到进阶
c++
ChoSeitaku2 小时前
NO.91十六届蓝桥杯备战|图论基础-图的存储和遍历|邻接矩阵|vector|链式前向星(C++)
c++·蓝桥杯·图论
虾球xz3 小时前
游戏引擎学习第223天
c++·学习·游戏引擎
ChiaWei Lee3 小时前
【C++初学】课后作业汇总复习(七) 指针-深浅copy
开发语言·c++
pixel33 小时前
C++构造函数
c++
444A4E3 小时前
C++多态完全指南:从虚函数到底层虚表机制,一文彻底掌握
c++·编译原理