组合模式(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
优缺点
| 说明 | |
|---|---|
| ✅ 优点 | 树形结构的天然实现方式 |
| ✅ 优点 | 客户端统一对待叶子和容器 |
| ✅ 优点 | 新增节点类型很方便 |
| ❌ 缺点 | 设计过于通用,类型安全性降低 |
| ❌ 缺点 | 不适合限制树的层次结构 |
一句话记忆
组合模式 = 文件夹里可以放文件也可以放文件夹,对外一律问"你多大",不管是不是叶子。