第11章 组合模式
11.1 一个基本的目录内容遍历范例
组合模式用于处理树形结构数据,如操作系统目录。以下是目录遍历的非组合模式实现:
文件类定义
class File {
public:File(string name) : m_sname(name) {}void ShowName(string lvlstr) {cout << lvlstr << "-" << m_sname << endl; // 用"-"表示文件}
private:string m_sname;
};
目录类定义
#include <list>
using namespace std;class Dir {
public:Dir(string name) : m_sname(name) {}void AddFile(File* pfile) { m_childFile.push_back(pfile); }void AddDir(Dir* pdir) { m_childDir.push_back(pdir); }void ShowName(string lvlstr) {cout << lvlstr << "+" << m_sname << endl; // 用"+"表示目录lvlstr += " "; // 缩进4个空格// 遍历文件for (auto iter = m_childFile.begin(); iter != m_childFile.end(); ++iter)(*iter)->ShowName(lvlstr);// 递归遍历子目录for (auto iter = m_childDir.begin(); iter != m_childDir.end(); ++iter)(*iter)->ShowName(lvlstr);}private:string m_sname;list<File*> m_childFile;list<Dir*> m_childDir;
};
主函数测试
int main() {// 1. 创建节点Dir* root = new Dir("root");File* f1 = new File("common.mk"), *f2 = new File("config.mk"), *f3 = new File("makefile");Dir* app = new Dir("app"), *signal = new Dir("signal"), *include = new Dir("include");File* f4 = new File("nginx.c"), *f5 = new File("ngx_conf.c"), *f6 = new File("ngx_signal.c");File* f7 = new File("ngx_func.h"), *f8 = new File("ngx_signal.h");// 2. 构建树结构root->AddFile(f1)->AddFile(f2)->AddFile(f3);root->AddDir(app)->AddFile(f4)->AddFile(f5);root->AddDir(signal)->AddFile(f6);root->AddDir(include)->AddFile(f7)->AddFile(f8);// 3. 遍历输出root->ShowName("");// 4. 释放资源(略)return 0;
}
输出结果
+root-common.mk-config.mk-makefile+app-nginx.c-ngx_conf.c+signal-ngx_signal.c+include-ngx_func.h-ngx_signal.h
11.2 使用组合模式改造范例
抽象组件类
class FileSystem {
public:virtual void ShowName(int level) = 0;virtual int Add(FileSystem* pfs) = 0;virtual int Remove(FileSystem* pfs) = 0;virtual ~FileSystem() {}
};
叶子组件(文件)
class File : public FileSystem {
public:File(string name) : m_sname(name) {}void ShowName(int level) override {cout << string(level*4, ' ') << "-" << m_sname << endl;}int Add(FileSystem* pfs) override { return -1; } // 文件不能添加子节点int Remove(FileSystem* pfs) override { return -1; }
private:string m_sname;
};
树枝组件(目录)
#include <list>
using namespace std;class Dir : public FileSystem {
public:Dir(string name) : m_sname(name) {}void ShowName(int level) override {cout << string(level*4, ' ') << "+" << m_sname << endl;for (auto& child : m_child)child->ShowName(level + 1);}int Add(FileSystem* pfs) override { m_child.push_back(pfs); return 0; }int Remove(FileSystem* pfs) override { m_child.remove(pfs); return 0; }private:string m_sname;list<FileSystem*> m_child;
};
改进后主函数
int main() {// 创建节点(使用基类指针)FileSystem* root = new Dir("root");auto add = [](FileSystem* parent, FileSystem* child) { parent->Add(child); };add(root, new File("common.mk"));add(root, new File("config.mk"));add(root, new File("makefile"));auto app = new Dir("app");add(root, app);add(app, new File("nginx.c"));add(app, new File("ngx_conf.c"));// 其他节点添加(类似)...root->ShowName(0); // 从根节点开始遍历return 0;
}
11.3 组合模式核心概念
模式定义
将对象组织成树形结构,以统一处理单个对象(叶子)和组合对象(树枝),实现部分-整体层次关系的一致操作。
UML 图
角色 | 说明 | 示例类 |
---|---|---|
Component | 抽象组件,定义公共接口 | FileSystem |
Leaf | 叶子节点,无子女 | File |
Composite | 树枝节点,包含子节点(叶/枝) | Dir |
关键特性
- 递归遍历:通过
Composite
的子节点列表实现树形递归 - 接口统一:客户端无需区分叶节点与枝节点
- 开闭原则:新增节点类型时只需扩展
Component
子类
11.4 透明模式 vs 安全模式
透明组合模式
- 特点:在抽象组件中声明所有接口(包括管理子节点的方法)
- 优点:客户端代码统一,完全面向抽象编程
- 缺点:叶子节点需实现无意义的方法(如
Add
)
安全组合模式
// 抽象组件(无管理子节点方法)
class FileSystem {
public:virtual void ShowName(int level) = 0;virtual bool IsComposite() { return false; } // 安全检查
};// 树枝组件(单独声明管理方法)
class Dir : public FileSystem {
public:void Add(FileSystem* pfs) { /* ... */ }bool IsComposite() override { return true; }
};
- 特点:管理子节点的方法仅在
Composite
中定义 - 优点:避免叶子节点误用
- 缺点:客户端需区分节点类型
UML
11.5 组合模式应用场景
1. 公司组织结构
class Organization : public FileSystem {
public:virtual int GetEmployeeCount() = 0;
};class Department : public Organization { /* 叶子节点 */ };
class Branch : public Organization { /* 树枝节点,含子部门/分公司 */ };
2. 图形编辑器
class Graphic {
public:virtual void Draw() = 0;
};class Circle : public Graphic { /* 叶子:绘制圆形 */ };
class Group : public Graphic { /* 树枝:包含多个图形 */ };
3. 杀毒软件
class File : public FileSystem {
public:virtual void ScanVirus() = 0;
};class ExeFile : public File { /* 特殊处理可执行文件 */ };
class Folder : public File { /* 目录扫描 */ };
总结:组合模式通过树形结构和统一接口,简化了分层数据的操作,是处理“部分-整体”关系的核心模式。