【设计模式】组合模式

发布于:2025-03-25 ⋅ 阅读:(30) ⋅ 点赞:(0)

第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 图

继承
继承
包含
1
*
FileSystem
+ShowName()
+Add()
+Remove()
File
+ShowName()
+Add()
+Remove()
Dir
-m_child
+ShowName()
+Add()
+Remove()
角色 说明 示例类
Component 抽象组件,定义公共接口 FileSystem
Leaf 叶子节点,无子女 File
Composite 树枝节点,包含子节点(叶/枝) Dir

关键特性

  1. 递归遍历:通过Composite的子节点列表实现树形递归
  2. 接口统一:客户端无需区分叶节点与枝节点
  3. 开闭原则:新增节点类型时只需扩展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

继承
继承
包含(通过 m_child)
1
*
FileSystem
+ShowName()
File
+ShowName()
Dir
-m_child
+ShowName()
+Add()
+Remove()
+GetChild()

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 { /* 树枝:包含多个图形 */ };

在这里插入图片描述

包含(通过 m_child)
1
*
Graphic
-m_child
+Draw()
+Add()
+Remove()
Line
+Draw()
+Add()
+Remove()
Rectangle
+Draw()
+Add()
+Remove()
Circle
+Draw()
+Add()
+Remove()
Text
+Draw()
+Add()
+Remove()
Picture
+Draw()
+Add()
+Remove()

3. 杀毒软件

class File : public FileSystem {
public:
    virtual void ScanVirus() = 0;
};

class ExeFile : public File { /* 特殊处理可执行文件 */ };
class Folder : public File { /* 目录扫描 */ };
继承
继承
继承
m_child
0..*
«abstract»
FileSystem
+KillVirus()
+Add()
+Remove()
ExecutableFile
+KillVirus()
+Add()
+Remove()
CommonFile
+KillVirus()
+Add()
+Remove()
Dir
-m_child: list<FileSystem>
+KillVirus()
+Add()
+Remove()

总结:组合模式通过树形结构和统一接口,简化了分层数据的操作,是处理“部分-整体”关系的核心模式。