组合模式 (Composite)
组合模式 是一种结构型设计模式,它将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使客户端对单个对象和组合对象的使用具有一致性。
意图
- 将对象组合成树形结构,以表示“部分-整体”的层次结构。
- 使得客户端可以以一致的方式处理单个对象和对象的组合。
使用场景
-
需要表示部分与整体的层次结构:
- 如文件系统、组织架构等。
-
客户端需要以一致的方式处理单个对象和组合对象:
- 统一处理方式简化了客户端的代码逻辑。
参与者角色
-
组件 (Component)
- 定义了组合对象和叶子对象的公共接口。
-
叶子 (Leaf)
- 表示层次结构的叶子节点,没有子节点。
-
组合 (Composite)
- 表示层次结构中的非叶子节点,存储子节点,并实现与子节点相关的操作。
-
客户端 (Client)
- 通过组件接口与对象的组合结构交互,无需关心对象是叶子还是组合对象。
示例代码
以下代码展示了如何使用组合模式实现文件系统的层次结构。
#include <iostream>
#include <vector>
#include <string>// 组件接口:定义叶子和组合对象的公共接口
class FileSystemComponent {
public:virtual ~FileSystemComponent() = default;virtual void display(int indent = 0) const = 0; // 显示组件内容的接口virtual void add(FileSystemComponent* component) {throw std::runtime_error("Cannot add to a leaf component.");}virtual void remove(FileSystemComponent* component) {throw std::runtime_error("Cannot remove from a leaf component.");}
};// 叶子类:文件
class File : public FileSystemComponent {
private:std::string name;public:File(const std::string& fileName) : name(fileName) {}void display(int indent = 0) const override {std::cout << std::string(indent, ' ') << "- File: " << name << std::endl;}
};// 组合类:目录
class Directory : public FileSystemComponent {
private:std::string name;std::vector<FileSystemComponent*> children; // 存储子节点public:Directory(const std::string& dirName) : name(dirName) {}~Directory() {for (auto child : children) {delete child;}}void display(int indent = 0) const override {std::cout << std::string(indent, ' ') << "+ Directory: " << name << std::endl;for (const auto& child : children) {child->display(indent + 2);}}void add(FileSystemComponent* component) override {children.push_back(component);}void remove(FileSystemComponent* component) override {children.erase(std::remove(children.begin(), children.end(), component), children.end());delete component;}
};// 客户端代码
int main() {// 创建目录和文件Directory* root = new Directory("root");Directory* subDir1 = new Directory("subDir1");Directory* subDir2 = new Directory("subDir2");File* file1 = new File("file1.txt");File* file2 = new File("file2.txt");File* file3 = new File("file3.txt");// 构建文件系统结构root->add(subDir1);root->add(subDir2);subDir1->add(file1);subDir1->add(file2);subDir2->add(file3);// 显示文件系统结构root->display();// 清理内存delete root;return 0;
}
代码解析
1. 组件接口 (FileSystemComponent)
- 定义了叶子和组合对象的公共接口,所有对象必须实现
display
方法。 - 提供了默认的
add
和remove
方法,叶子类中默认抛出异常。
class FileSystemComponent {
public:virtual ~FileSystemComponent() = default;virtual void display(int indent = 0) const = 0;virtual void add(FileSystemComponent* component) {throw std::runtime_error("Cannot add to a leaf component.");}virtual void remove(FileSystemComponent* component) {throw std::runtime_error("Cannot remove from a leaf component.");}
};
2. 叶子类 (File)
- 表示树结构的叶子节点,例如文件。叶子节点没有子节点。
class File : public FileSystemComponent {
private:std::string name;public:File(const std::string& fileName) : name(fileName) {}void display(int indent = 0) const override {std::cout << std::string(indent, ' ') << "- File: " << name << std::endl;}
};
3. 组合类 (Directory)
- 表示树结构的非叶子节点,例如目录。组合对象可以包含叶子节点和其他组合对象。
- 提供
add
和remove
方法,用于添加或移除子节点。
class Directory : public FileSystemComponent {
private:std::string name;std::vector<FileSystemComponent*> children;public:Directory(const std::string& dirName) : name(dirName) {}~Directory() {for (auto child : children) {delete child;}}void display(int indent = 0) const override {std::cout << std::string(indent, ' ') << "+ Directory: " << name << std::endl;for (const auto& child : children) {child->display(indent + 2);}}void add(FileSystemComponent* component) override {children.push_back(component);}void remove(FileSystemComponent* component) override {children.erase(std::remove(children.begin(), children.end(), component), children.end());delete component;}
};
4. 客户端
- 客户端通过
FileSystemComponent
接口与文件系统交互,不需要关心具体是叶子对象还是组合对象。
Directory* root = new Directory("root");
Directory* subDir1 = new Directory("subDir1");
File* file1 = new File("file1.txt");root->add(subDir1);
subDir1->add(file1);
root->display(); // 显示文件系统结构
优缺点
优点
- 统一接口:
- 客户端可以以一致的方式处理单个对象和组合对象。
- 灵活性高:
- 可以很容易地扩展新类型的叶子节点或组合对象。
- 简化客户端代码:
- 客户端不需要了解对象的具体类型,简化了代码逻辑。
缺点
- 可能导致系统复杂性增加:
- 如果对象种类较多,系统会变得更加复杂。
- 对子节点的管理较难:
- 子节点的添加和删除操作可能需要额外的逻辑。
适用场景
-
需要表示部分与整体的层次结构:
- 如图形系统中的图形组件、文件系统中的目录和文件。
-
客户端需要统一对待单个对象和组合对象:
- 组合模式可以简化客户端的操作逻辑。
总结
组合模式通过将对象组合成树形结构,使得客户端能够统一地处理单个对象和组合对象,特别适用于表示部分-整体层次结构的场景。