自学内容网 自学内容网

C++实现设计模式---组合模式 (Composite)

组合模式 (Composite)

组合模式 是一种结构型设计模式,它将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使客户端对单个对象和组合对象的使用具有一致性。


意图

  • 将对象组合成树形结构,以表示“部分-整体”的层次结构。
  • 使得客户端可以以一致的方式处理单个对象和对象的组合。

使用场景

  1. 需要表示部分与整体的层次结构

    • 如文件系统、组织架构等。
  2. 客户端需要以一致的方式处理单个对象和组合对象

    • 统一处理方式简化了客户端的代码逻辑。

参与者角色

  1. 组件 (Component)

    • 定义了组合对象和叶子对象的公共接口。
  2. 叶子 (Leaf)

    • 表示层次结构的叶子节点,没有子节点。
  3. 组合 (Composite)

    • 表示层次结构中的非叶子节点,存储子节点,并实现与子节点相关的操作。
  4. 客户端 (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 方法。
  • 提供了默认的 addremove 方法,叶子类中默认抛出异常。
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)
  • 表示树结构的非叶子节点,例如目录。组合对象可以包含叶子节点和其他组合对象。
  • 提供 addremove 方法,用于添加或移除子节点。
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(); // 显示文件系统结构

优缺点

优点
  1. 统一接口
    • 客户端可以以一致的方式处理单个对象和组合对象。
  2. 灵活性高
    • 可以很容易地扩展新类型的叶子节点或组合对象。
  3. 简化客户端代码
    • 客户端不需要了解对象的具体类型,简化了代码逻辑。
缺点
  1. 可能导致系统复杂性增加
    • 如果对象种类较多,系统会变得更加复杂。
  2. 对子节点的管理较难
    • 子节点的添加和删除操作可能需要额外的逻辑。

适用场景

  1. 需要表示部分与整体的层次结构

    • 如图形系统中的图形组件、文件系统中的目录和文件。
  2. 客户端需要统一对待单个对象和组合对象

    • 组合模式可以简化客户端的操作逻辑。

总结

组合模式通过将对象组合成树形结构,使得客户端能够统一地处理单个对象和组合对象,特别适用于表示部分-整体层次结构的场景。


原文地址:https://blog.csdn.net/Johnor/article/details/145232330

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!