当前位置: 首页 > news >正文

组合模式及优化

组合模式是一种结构型设计模式,其核心思想是将对象组合成树形结构,以表示“部分-整体”的层次关系,使得用户对单个对象和组合对象的使用具有一致性。

一、介绍

核心角色

组合模式包含以下3个关键角色:

  1. 抽象组件(Component)
    定义单个对象和组合对象的共同接口,声明所有操作(如添加、删除子节点、获取子节点等)。
  2. 叶子节点(Leaf)
    表示树形结构中的“单个对象”,没有子节点。实现抽象组件的接口,但不支持“添加/删除子节点”等操作(通常抛出异常)。
  3. 复合节点(Composite)
    表示树形结构中的“组合对象”,可以包含子节点(叶子节点或其他复合节点)。实现抽象组件的接口,并重写“添加/删除子节点”等操作,通过管理子节点集合实现功能。
优点
  1. 一致性操作:用户无需区分单个对象和组合对象,统一调用接口即可处理整个树形结构。
  2. 扩展性强:新增叶子节点或复合节点时,无需修改现有代码(符合开闭原则)。
  3. 简化客户端逻辑:客户端无需编写复杂的判断逻辑(如“是否为组合对象”),直接递归处理即可。
  4. 清晰表示层次关系:通过树形结构直观体现“部分-整体”关系,便于理解和维护。
适用场景

当需要处理 具有“部分-整体”层次关系的对象结构,且希望用户忽略单个对象和组合对象的差异时,适合使用组合模式。典型场景包括:

  • 树形结构数据:如文件系统(文件夹与文件)、组织机构(部门与员工)、XML/JSON节点等。
  • UI组件:如按钮(叶子)和面板(复合,包含按钮/其他面板)。
  • 图形绘制:如基本图形(直线、圆)和组合图形(由多个基本图形组成)。

二、实现

以文件系统的为例,使用组合模式表示文件和文件夹的层次结构:

#include <iostream>
#include <vector>
#include <string>
#include <memory>// 抽象组件类:定义文件和文件夹的共同接口
class FileSystemComponent {
protected:std::string name_;public:explicit FileSystemComponent(std::string name) : name_(std::move(name)) {}virtual ~FileSystemComponent() = default;// 获取名称std::string getName() const {return name_;}// 纯虚函数:显示组件信息(声明为纯虚函数,使该类成为抽象类)virtual void display(int depth = 0) const = 0;// 虚函数:添加子组件(默认不实现,由容器类重写)virtual void add(std::shared_ptr<FileSystemComponent> component) {throw std::runtime_error("不支持添加操作");}// 虚函数:移除子组件(默认不实现,由容器类重写)virtual void remove(const std::string& name) {throw std::runtime_error("不支持移除操作");}// 虚函数:获取子组件(默认不实现,由容器类重写)virtual std::shared_ptr<FileSystemComponent> getChild(const std::string& name) {throw std::runtime_error("不支持获取子组件操作");}
};// 叶子节点:文件
class File : public FileSystemComponent {
private:int size_;  // 文件大小(KB)public:File(std::string name, int size) : FileSystemComponent(std::move(name)), size_(size) {}// 显示文件信息void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "文件: " << name_ << " (" << size_ << "KB)" << std::endl;}
};// 容器节点:文件夹
class Folder : public FileSystemComponent {
private:std::vector<std::shared_ptr<FileSystemComponent>> children_;public:explicit Folder(std::string name) : FileSystemComponent(std::move(name)) {}// 添加子组件(文件或文件夹)void add(std::shared_ptr<FileSystemComponent> component) override {children_.push_back(std::move(component));}// 移除子组件void remove(const std::string& name) override {auto it = std::remove_if(children_.begin(), children_.end(),[&name](const std::shared_ptr<FileSystemComponent>& comp) {return comp->getName() == name;});if (it != children_.end()) {children_.erase(it, children_.end());}}// 获取子组件std::shared_ptr<FileSystemComponent> getChild(const std::string& name) override {for (const auto& child : children_) {if (child->getName() == name) {return child;}}return nullptr;}// 显示文件夹信息及所有子组件void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "文件夹: " << name_ << " (包含 " << children_.size() << " 个项目)" << std::endl;// 递归显示子组件,深度+1for (const auto& child : children_) {child->display(depth + 2);}}
};// 客户端代码
int main() {// 创建文件auto file1 = std::make_shared<File>("readme.txt", 10);auto file2 = std::make_shared<File>("image.png", 2048);auto file3 = std::make_shared<File>("data.csv", 512);auto file4 = std::make_shared<File>("notes.txt", 5);// 创建文件夹auto docsFolder = std::make_shared<Folder>("文档");auto picsFolder = std::make_shared<Folder>("图片");auto rootFolder = std::make_shared<Folder>("根目录");// 构建文件系统结构docsFolder->add(file1);docsFolder->add(file4);picsFolder->add(file2);rootFolder->add(docsFolder);rootFolder->add(picsFolder);rootFolder->add(file3);// 显示整个文件系统(通过根节点统一操作)std::cout << "文件系统结构:" << std::endl;rootFolder->display();return 0;
}   
输出结果
文件系统结构:
文件夹: 根目录 (包含 3 个项目)
--文件夹: 文档 (包含 2 个项目)
----文件: readme.txt (10KB)
----文件: notes.txt (5KB)
--文件夹: 图片 (包含 1 个项目)
----文件: image.png (2048KB)
--文件: data.csv (512KB)
应用场景
  1. 文件系统
    • 文件夹和文件组成的树形结构,支持统一的操作接口
  2. UI框架
    • 容器控件(如面板、窗口)包含其他控件(如按钮、文本框),形成树形结构
  3. 组织结构
    • 公司包含部门,部门包含小组,小组包含员工,形成层次结构
  4. 图形系统
    • 复杂图形由简单图形组合而成,如组合图形(CompositeShape)包含多个基本图形(Circle、Rectangle)
  5. 菜单系统
    • 菜单栏包含菜单,菜单包含菜单项或子菜单,形成树形结构

三、优化

优化点
  1. 泛型设计
    • 使用模板实现通用组件,支持任意数据类型(示例中用int表示文件大小)
    • 同一套组合模式可适用于不同业务场景(文件系统、UI组件、组织机构等)
  2. 类型安全与错误处理
    • 增加组件类型判断(isComposite()
    • 防止添加空组件、自身作为子组件等非法操作
    • 使用异常机制处理错误,提供更友好的错误信息
  3. 迭代器支持
    • 实现ComponentIterator接口,支持统一遍历组合组件
    • 客户端可通过迭代器访问子组件,无需了解内部存储结构
  4. 功能扩展接口
    • 增加getSize()方法,支持递归计算总大小
    • 组合组件添加countLeaves()方法,统计叶子节点数量
    • 保留扩展空间,可根据需求添加更多聚合操作
  5. 现代C++特性
    • 使用std::shared_ptr管理组件生命周期,避免内存泄漏
    • 利用STL算法(accumulateremove_if)简化代码
    • 使用override关键字明确重写关系,增强代码可读性
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <iterator>
#include <numeric>
#include <typeinfo>
#include <stdexcept>// 前向声明
template <typename T>
class Component;// 迭代器接口 - 支持遍历组件
template <typename T>
class ComponentIterator {
public:using iterator_category = std::forward_iterator_tag;using value_type = std::shared_ptr<Component<T>>;using difference_type = std::ptrdiff_t;using pointer = value_type*;using reference = value_type&;virtual ~ComponentIterator() = default;virtual bool hasNext() const = 0;virtual value_type next() = 0;virtual void reset() = 0;
};// 抽象组件基类 - 泛型设计支持不同类型组件
template <typename T>
class Component {
protected:std::string name_;T data_;  // 组件携带的数据public:explicit Component(std::string name, T data = T{}) : name_(std::move(name)), data_(std::move(data)) {}virtual ~Component() = default;// 基础接口std::string getName() const { return name_; }T getData() const { return data_; }void setData(T data) { data_ = std::move(data); }// 纯虚接口 - 必须实现virtual void display(int depth = 0) const = 0;virtual bool isComposite() const = 0;  // 区分叶子和组合// 组合操作 - 默认抛出异常,组合组件需重写virtual void add(std::shared_ptr<Component<T>>) {throw std::runtime_error("不支持添加操作: " + name_);}virtual void remove(const std::string& name) {throw std::runtime_error("不支持移除操作: " + name_);}virtual std::shared_ptr<Component<T>> getChild(const std::string& name) {throw std::runtime_error("不支持获取子组件: " + name_);}// 迭代器支持virtual std::unique_ptr<ComponentIterator<T>> createIterator() {throw std::runtime_error("不支持迭代器: " + name_);}// 功能扩展接口 - 计算组件大小(示例)virtual size_t getSize() const = 0;
};// 叶子组件 - 不能包含子组件
template <typename T>
class Leaf : public Component<T> {
public:Leaf(std::string name, T data = T{}) : Component<T>(std::move(name), std::move(data)) {}void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "叶子: " << this->name_ << " (数据: " << this->data_ << ")" << std::endl;}bool isComposite() const override { return false; }// 叶子组件大小即为自身大小size_t getSize() const override {return sizeof(*this);  // 实际应用中可返回真实数据大小}
};// 组合组件迭代器实现
template <typename T>
class CompositeIterator : public ComponentIterator<T> {
private:std::vector<std::shared_ptr<Component<T>>> children_;size_t currentIndex_ = 0;public:explicit CompositeIterator(std::vector<std::shared_ptr<Component<T>>> children): children_(std::move(children)) {}bool hasNext() const override {return currentIndex_ < children_.size();}typename ComponentIterator<T>::value_type next() override {if (!hasNext()) {throw std::out_of_range("迭代器已到达末尾");}return children_[currentIndex_++];}void reset() override {currentIndex_ = 0;}
};// 组合组件 - 可以包含子组件
template <typename T>
class Composite : public Component<T> {
private:std::vector<std::shared_ptr<Component<T>>> children_;// 类型检查辅助函数template <typename U>bool isType(const std::shared_ptr<Component<T>>& component) const {return typeid(*component) == typeid(U);}public:explicit Composite(std::string name, T data = T{}) : Component<T>(std::move(name), std::move(data)) {}// 添加子组件(带类型检查)void add(std::shared_ptr<Component<T>> component) override {if (!component) {throw std::invalid_argument("不能添加空组件");}if (component.get() == this) {throw std::invalid_argument("不能添加自身作为子组件");}children_.push_back(std::move(component));}// 移除子组件void remove(const std::string& name) override {auto it = std::remove_if(children_.begin(), children_.end(),[&name](const std::shared_ptr<Component<T>>& comp) {return comp->getName() == name;});if (it != children_.end()) {children_.erase(it, children_.end());} else {throw std::out_of_range("未找到子组件: " + name);}}// 获取子组件std::shared_ptr<Component<T>> getChild(const std::string& name) override {for (const auto& child : children_) {if (child->getName() == name) {return child;}}return nullptr;}// 显示组件及子组件void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "组合: " << this->name_ << " (包含 " << children_.size() << " 个子组件)" << std::endl;// 递归显示子组件for (const auto& child : children_) {child->display(depth + 2);}}bool isComposite() const override { return true; }// 创建迭代器std::unique_ptr<ComponentIterator<T>> createIterator() override {return std::make_unique<CompositeIterator<T>>(children_);}// 计算总大小(递归计算所有子组件)size_t getSize() const override {return std::accumulate(children_.begin(), children_.end(), sizeof(*this),  // 自身大小[](size_t total, const std::shared_ptr<Component<T>>& child) {return total + child->getSize();});}// 扩展功能:统计叶子节点数量size_t countLeaves() const {size_t count = 0;for (const auto& child : children_) {if (child->isComposite()) {// 向下转型调用组合组件的方法auto composite = std::dynamic_pointer_cast<Composite<T>>(child);if (composite) {count += composite->countLeaves();}} else {count++;}}return count;}
};// 客户端代码 - 文件系统示例
int main() {try {// 创建文件(叶子组件,数据为文件大小KB)auto file1 = std::make_shared<Leaf<int>>("readme.txt", 10);auto file2 = std::make_shared<Leaf<int>>("image.png", 2048);auto file3 = std::make_shared<Leaf<int>>("data.csv", 512);auto file4 = std::make_shared<Leaf<int>>("notes.txt", 5);// 创建文件夹(组合组件)auto docsFolder = std::make_shared<Composite<int>>("文档");auto picsFolder = std::make_shared<Composite<int>>("图片");auto rootFolder = std::make_shared<Composite<int>>("根目录");// 构建层次结构docsFolder->add(file1);docsFolder->add(file4);picsFolder->add(file2);rootFolder->add(docsFolder);rootFolder->add(picsFolder);rootFolder->add(file3);// 显示整个结构std::cout << "文件系统结构:" << std::endl;rootFolder->display();// 使用迭代器遍历根目录子组件std::cout << "\n根目录子组件列表:" << std::endl;auto iterator = rootFolder->createIterator();while (iterator->hasNext()) {auto comp = iterator->next();std::cout << "- " << comp->getName() << (comp->isComposite() ? " (文件夹)" : " (文件)") << std::endl;}// 功能扩展演示std::cout << "\n统计信息:" << std::endl;std::cout << "总大小: " << rootFolder->getSize() << " 字节" << std::endl;std::cout << "文件总数: " << rootFolder->countLeaves() << " 个" << std::endl;// 测试错误处理try {file1->add(file2);  // 叶子组件不能添加子组件} catch (const std::exception& e) {std::cout << "\n错误处理测试: " << e.what() << std::endl;}} catch (const std::exception& e) {std::cerr << "发生错误: " << e.what() << std::endl;return 1;}return 0;
}   
输出结果
文件系统结构:
组合: 根目录 (包含 3 个子组件)
--组合: 文档 (包含 2 个子组件)
----叶子: readme.txt (数据: 10)
----叶子: notes.txt (数据: 5)
--组合: 图片 (包含 1 个子组件)
----叶子: image.png (数据: 2048)
--叶子: data.csv (数据: 512)根目录子组件列表:
- 文档 (文件夹)
- 图片 (文件夹)
- data.csv (文件)统计信息:
总大小: 40 字节
文件总数: 4 个错误处理测试: 不支持添加操作: readme.txt
适用场景扩展

优化后的组合模式更适合:

  • 复杂树形结构的管理(如多级菜单、嵌套控件)
  • 需要统一遍历接口的场景(如递归计算、搜索)
  • 频繁扩展功能的系统(通过扩展接口添加新操作)
  • 对类型安全和内存管理有严格要求的生产环境
http://www.dtcms.com/a/332744.html

相关文章:

  • 新手向:Python循环结构(for/while)及控制语句(break/continue)
  • 中国象棋人机对战
  • 使用转换函数重载布尔值类
  • 申请第二个域名还要备案吗
  • 《软件工程导论》实验报告四 详细设计工具
  • 两幅美国国旗版权挂钩专利发起跨境诉讼
  • 云原生俱乐部-杂谈2
  • 机器学习之PCA降维
  • uniapp 开发微信小程序,获取经纬度并且转化详细地址(单独封装版本)
  • week1-[顺序结构]跑道
  • IStoreOS(OpenWrt)开启IPV6
  • 设备数据采集服务器软件TOP Server OPC Server详细介绍
  • wsl安装完美教程
  • Vulnhub Deathnote靶机复现攻略
  • 告别手动优化!React Compiler 自动记忆化技术深度解析
  • 16进制pcm数据转py波形脚本
  • Vim 常用快捷键及插件
  • 关于simplifyweibo_4_moods数据集的分类问题
  • 大白话解析“入口点合约”
  • Linux系统--库制作与原理
  • Java—注解
  • mysql-条件查询案例
  • zabbix部署问题后常见问题
  • Codeforces 无路可走
  • 分布式系统设计的容错机制
  • AI优质信息源汇总:含X账号,Newsletter,播客,App
  • 如何在 FastAPI 中玩转 APScheduler,让任务定时自动执行?
  • 上下文块嵌入(contextualized-chunk-embeddings)
  • collections:容器数据类型
  • C语言——深入理解指针(四)