23种设计模式 - 组合模式(Composite)

组合模式(Composite)------ 文件夹套文件夹,统一操作

大白话解释

你的电脑里:

  • 📄 文件(不能再包含东西)
  • 📁 文件夹(可以装文件,也可以装文件夹)

👉 关键点来了:

不管是文件还是文件夹,你都可以:

  • 查看大小
  • 打印信息

组合模式:把对象组合成树形结构,让单个对象和组合对象的使用方式一致。你可以对整棵树统一操作,不用关心里面是叶子还是容器。

常见场景:

  • 文件系统(文件 + 文件夹)
  • UI 组件树(按钮、面板、容器)
  • 部门组织架构(员工 + 部门)
  • XML/HTML DOM 树

C++ 代码示例

场景:文件系统,统一计算大小、统一打印目录树。

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

// ==============================
// 组件接口:文件和文件夹都实现
// ==============================
class FileSystemNode {
public:
    virtual ~FileSystemNode() = default;
    virtual std::string getName() const = 0;
    virtual long long getSize() const = 0;
    virtual void print(const std::string& indent = "") const = 0;
    
    // 对于叶子节点(文件),这些操作无意义,默认什么都不做
    virtual void add(std::shared_ptr<FileSystemNode> node) {
        std::cout << "错误:不能向文件添加子节点\n";
    }
    virtual void remove(const std::string& name) {}
};

// ==============================
// 叶子节点:文件
// ==============================
class File : public FileSystemNode {
private:
    std::string name;
    long long size; // 字节

public:
    File(const std::string& n, long long s) : name(n), size(s) {}

    std::string getName() const override { return name; }
    long long getSize() const override { return size; }

    void print(const std::string& indent = "") const override {
        std::cout << indent << "📄 " << name 
                  << " (" << size << " bytes)\n";
    }
};

// ==============================
// 容器节点:文件夹
// ==============================
class Folder : public FileSystemNode {
private:
    std::string name;
    std::vector<std::shared_ptr<FileSystemNode>> children;

public:
    Folder(const std::string& n) : name(n) {}

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

    // 递归计算大小:所有子节点大小之和
    long long getSize() const override {
        long long total = 0;
        for (const auto& child : children) {
            total += child->getSize();
        }
        return total;
    }

    void add(std::shared_ptr<FileSystemNode> node) override {
        children.push_back(node);
    }

    void remove(const std::string& n) override {
        children.erase(
            std::remove_if(children.begin(), children.end(),
                [&n](const auto& c) { return c->getName() == n; }),
            children.end()
        );
    }

    // 递归打印目录树
    void print(const std::string& indent = "") const override {
        std::cout << indent << "📁 " << name 
                  << " (" << getSize() << " bytes)\n";
        for (const auto& child : children) {
            child->print(indent + "  "); // 子节点多缩进两格
        }
    }
};

int main() {
    // 构建目录树
    auto root = std::make_shared<Folder>("项目根目录");

    auto src = std::make_shared<Folder>("src");
    src->add(std::make_shared<File>("main.cpp", 2048));
    src->add(std::make_shared<File>("utils.cpp", 1536));
    src->add(std::make_shared<File>("utils.h", 512));

    auto tests = std::make_shared<Folder>("tests");
    tests->add(std::make_shared<File>("test_main.cpp", 1024));
    tests->add(std::make_shared<File>("test_utils.cpp", 768));

    auto docs = std::make_shared<Folder>("docs");
    docs->add(std::make_shared<File>("README.md", 4096));
    docs->add(std::make_shared<File>("API.md", 8192));

    auto assets = std::make_shared<Folder>("assets");
    auto images = std::make_shared<Folder>("images");
    images->add(std::make_shared<File>("logo.png", 102400));
    images->add(std::make_shared<File>("banner.jpg", 204800));
    assets->add(images);
    assets->add(std::make_shared<File>("style.css", 10240));

    root->add(src);
    root->add(tests);
    root->add(docs);
    root->add(assets);
    root->add(std::make_shared<File>(".gitignore", 256));

    // 打印目录树
    std::cout << "=== 目录结构 ===\n";
    root->print();

    // 统一接口:对文件夹和文件都能问大小
    std::cout << "\n=== 各目录大小 ===\n";
    std::cout << "src 目录: " << src->getSize() << " bytes\n";
    std::cout << "docs 目录: " << docs->getSize() << " bytes\n";
    std::cout << "assets 目录: " << assets->getSize() << " bytes\n";
    std::cout << "整个项目: " << root->getSize() << " bytes\n";

    return 0;
}

输出:

复制代码
=== 目录结构 ===
📁 项目根目录 (335872 bytes)
  📁 src (4096 bytes)
    📄 main.cpp (2048 bytes)
    📄 utils.cpp (1536 bytes)
    📄 utils.h (512 bytes)
  📁 tests (1792 bytes)
    📄 test_main.cpp (1024 bytes)
    📄 test_utils.cpp (768 bytes)
  📁 docs (12288 bytes)
    📄 README.md (4096 bytes)
    📄 API.md (8192 bytes)
  📁 assets (317440 bytes)
    📁 images (307200 bytes)
      📄 logo.png (102400 bytes)
      📄 banner.jpg (204800 bytes)
    📄 style.css (10240 bytes)
  📄 .gitignore (256 bytes)

=== 各目录大小 ===
src 目录: 4096 bytes
docs 目录: 12288 bytes
assets 目录: 317440 bytes
整个项目: 335872 bytes

优缺点

说明
✅ 优点 树形结构的天然实现方式
✅ 优点 客户端统一对待叶子和容器
✅ 优点 新增节点类型很方便
❌ 缺点 设计过于通用,类型安全性降低
❌ 缺点 不适合限制树的层次结构

一句话记忆

组合模式 = 文件夹里可以放文件也可以放文件夹,对外一律问"你多大",不管是不是叶子。

相关推荐
WarrenMondeville2 小时前
1.Unity面向对象-单一职责原则
unity·设计模式·c#
bmseven3 小时前
23种设计模式 - 适配器模式(Adapter)
设计模式·适配器模式
MarkHD5 小时前
RPA工程化实践:三种核心设计模式让复杂流程优雅可控
linux·设计模式·rpa
AI大法师6 小时前
字标Logo设计指南:中文品牌如何用字体做出高级感与辨识度
人工智能·设计模式
Yu_Lijing6 小时前
基于C++的《Head First设计模式》笔记——中介者模式
笔记·设计模式·中介者模式
程序员小寒7 小时前
JavaScript设计模式(四):发布-订阅模式实现与应用
开发语言·前端·javascript·设计模式
是糖糖啊9 小时前
Agent 不好用?先别怪模型,试试 Harness Engineering
人工智能·设计模式
jiankeljx9 小时前
Spring Boot 经典九设计模式全览
java·spring boot·设计模式
WarrenMondeville9 小时前
5.Unity面向对象-依赖倒置原则
unity·设计模式·依赖倒置原则