组合模式(Composite Pattern)及其应用场景
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
模式定义
组合模式通过将对象组合成树状结构来表示部分-整体的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
模式结构
Component(抽象构件): 定义叶子和容器构件的共同接口
Leaf(叶子构件): 表示叶节点对象,没有子节点
Composite(容器构件): 表示容器节点对象,存储子部件并实现与子部件相关的操作
Client(客户端): 通过Component接口操作组合结构中的对象
应用场景
需要表示对象的"部分-整体"层次结构时
希望用户忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时
处理树形结构数据时
当系统需要动态地在层次结构中添加新的构件时
优点
可以清楚地定义分层次的复杂对象
客户端可以一致地使用组合结构和单个对象
更容易在组合体内加入新的构件
简化了客户端代码,客户端可以一致地处理简单元素和复杂元素
缺点
设计较复杂,客户端需要花更多时间理清类之间的层次关系
很难限制组合中的构件类型
在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则
C++实用示例
示例1:文件系统实现
#include <iostream>
#include <string>
#include <vector>
#include <memory>// 抽象构件
class FileSystemComponent {
public:virtual ~FileSystemComponent() = default;virtual void display(int depth = 0) const = 0;virtual void add(std::unique_ptr<FileSystemComponent> component) {throw std::runtime_error("Cannot add to a leaf component");}
};// 叶子构件 - 文件
class File : public FileSystemComponent {std::string name;
public:explicit File(const std::string& name) : name(name) {}void display(int depth = 0) const override {std::cout << std::string(depth * 2, ' ') << "- " << name << std::endl;}
};// 容器构件 - 目录
class Directory : public FileSystemComponent {std::string name;std::vector<std::unique_ptr<FileSystemComponent>> children;
public:explicit Directory(const std::string& name) : name(name) {}void display(int depth = 0) const override {std::cout << std::string(depth * 2, ' ') << "+ " << name << std::endl;for (const auto& child : children) {child->display(depth + 1);}}void add(std::unique_ptr<FileSystemComponent> component) override {children.push_back(std::move(component));}
};int main() {auto root = std::make_unique<Directory>("Root");auto documents = std::make_unique<Directory>("Documents");documents->add(std::make_unique<File>("Resume.pdf"));documents->add(std::make_unique<File>("CoverLetter.docx"));auto pictures = std::make_unique<Directory>("Pictures");pictures->add(std::make_unique<File>("Vacation.jpg"));pictures->add(std::make_unique<File>("Family.png"));auto downloads = std::make_unique<Directory>("Downloads");downloads->add(std::make_unique<File>("Setup.exe"));root->add(std::move(documents));root->add(std::move(pictures));root->add(std::make_unique<File>("README.txt"));root->add(std::move(downloads));root->display();
}
示例2:图形绘制系统
#include <iostream>
#include <vector>
#include <memory>
#include <cmath>// 抽象构件
class Graphic {
public:virtual ~Graphic() = default;virtual void move(int x, int y) = 0;virtual void draw() const = 0;
};// 叶子构件 - 点
class Dot : public Graphic {int x, y;
public:Dot(int x, int y) : x(x), y(y) {}void move(int x, int y) override {this->x += x;this->y += y;}void draw() const override {std::cout << "Drawing a dot at (" << x << ", " << y << ")\n";}
};// 叶子构件 - 圆
class Circle : public Graphic {int x, y;int radius;
public:Circle(int x, int y, int radius) : x(x), y(y), radius(radius) {}void move(int x, int y) override {this->x += x;this->y += y;}void draw() const override {std::cout << "Drawing a circle at (" << x << ", " << y << ") with radius " << radius << "\n";}
};// 容器构件 - 组合图形
class CompoundGraphic : public Graphic {std::vector<std::unique_ptr<Graphic>> children;
public:void add(std::unique_ptr<Graphic> graphic) {children.push_back(std::move(graphic));}void move(int x, int y) override {for (const auto& child : children) {child->move(x, y);}}void draw() const override {std::cout << "=== Drawing a compound graphic ===\n";for (const auto& child : children) {child->draw();}std::cout << "=== End of compound graphic ===\n";}
};int main() {auto all = std::make_unique<CompoundGraphic>();all->add(std::make_unique<Dot>(1, 2));all->add(std::make_unique<Circle>(5, 3, 10));auto group = std::make_unique<CompoundGraphic>();group->add(std::make_unique<Circle>(10, 10, 5));group->add(std::make_unique<Dot>(10, 10));all->add(std::move(group));all->add(std::make_unique<Dot>(20, 20));std::cout << "Initial drawing:\n";all->draw();all->move(5, 5);std::cout << "\nAfter moving (5,5):\n";all->draw();
}
示例3:组织架构系统
#include <iostream>
#include <vector>
#include <memory>
#include <string>// 抽象构件
class OrganizationUnit {
public:virtual ~OrganizationUnit() = default;virtual void display(int depth = 0) const = 0;virtual void add(std::unique_ptr<OrganizationUnit> unit) {throw std::runtime_error("Cannot add to a leaf unit");}virtual double getSalary() const = 0;
};// 叶子构件 - 员工
class Employee : public OrganizationUnit {std::string name;double salary;
public:Employee(const std::string& name, double salary) : name(name), salary(salary) {}void display(int depth = 0) const override {std::cout << std::string(depth * 2, ' ') << name << " (Salary: $" << salary << ")\n";}double getSalary() const override { return salary; }
};// 容器构件 - 部门
class Department : public OrganizationUnit {std::string name;std::vector<std::unique_ptr<OrganizationUnit>> units;
public:explicit Department(const std::string& name) : name(name) {}void display(int depth = 0) const override {std::cout << std::string(depth * 2, ' ') << "Department: " << name << " (Total Salary: $" << getSalary() << ")\n";for (const auto& unit : units) {unit->display(depth + 1);}}void add(std::unique_ptr<OrganizationUnit> unit) override {units.push_back(std::move(unit));}double getSalary() const override {double total = 0.0;for (const auto& unit : units) {total += unit->getSalary();}return total;}
};int main() {auto company = std::make_unique<Department>("ACME Corporation");auto engineering = std::make_unique<Department>("Engineering");engineering->add(std::make_unique<Employee>("John Doe", 85000));engineering->add(std::make_unique<Employee>("Jane Smith", 95000));auto qa = std::make_unique<Department>("Quality Assurance");qa->add(std::make_unique<Employee>("Bob Johnson", 75000));qa->add(std::make_unique<Employee>("Alice Williams", 80000));engineering->add(std::move(qa));company->add(std::move(engineering));auto marketing = std::make_unique<Department>("Marketing");marketing->add(std::make_unique<Employee>("Mike Brown", 90000));marketing->add(std::make_unique<Employee>("Sarah Davis", 88000));company->add(std::move(marketing));company->add(std::make_unique<Employee>("CEO", 250000));std::cout << "Company Structure:\n";company->display();std::cout << "\nTotal company salary: $" << company->getSalary() << std::endl;
}
示例4:UI组件系统
#include <iostream>
#include <vector>
#include <memory>
#include <string>// 抽象构件
class UIComponent {
public:virtual ~UIComponent() = default;virtual void render() const = 0;virtual void add(std::unique_ptr<UIComponent> component) {throw std::runtime_error("Cannot add to a leaf component");}
};// 叶子构件 - 按钮
class Button : public UIComponent {std::string label;
public:explicit Button(const std::string& label) : label(label) {}void render() const override {std::cout << "[Button: " << label << "]\n";}
};// 叶子构件 - 文本框
class TextBox : public UIComponent {std::string placeholder;
public:explicit TextBox(const std::string& placeholder) : placeholder(placeholder) {}void render() const override {std::cout << "[TextBox: " << placeholder << "]\n";}
};// 容器构件 - 面板
class Panel : public UIComponent {std::string title;std::vector<std::unique_ptr<UIComponent>> children;
public:explicit Panel(const std::string& title) : title(title) {}void render() const override {std::cout << "=== Panel: " << title << " ===\n";for (const auto& child : children) {child->render();}std::cout << "=== End of Panel ===\n";}void add(std::unique_ptr<UIComponent> component) override {children.push_back(std::move(component));}
};// 容器构件 - 窗口
class Window : public UIComponent {std::string title;std::vector<std::unique_ptr<UIComponent>> children;
public:explicit Window(const std::string& title) : title(title) {}void render() const override {std::cout << "**** Window: " << title << " ****\n";for (const auto& child : children) {child->render();}std::cout << "**** End of Window ****\n";}void add(std::unique_ptr<UIComponent> component) override {children.push_back(std::move(component));}
};int main() {auto loginWindow = std::make_unique<Window>("Login");auto formPanel = std::make_unique<Panel>("Login Form");formPanel->add(std::make_unique<TextBox>("Username"));formPanel->add(std::make_unique<TextBox>("Password"));auto buttonPanel = std::make_unique<Panel>("Actions");buttonPanel->add(std::make_unique<Button>("Login"));buttonPanel->add(std::make_unique<Button>("Cancel"));loginWindow->add(std::move(formPanel));loginWindow->add(std::move(buttonPanel));std::cout << "Rendering UI:\n";loginWindow->render();
}
这些示例展示了组合模式如何通过树形结构来表示部分-整体层次结构,使得客户端可以一致地处理单个对象和组合对象。组合模式特别适合用于构建递归结构,如文件系统、UI组件、组织结构等场景。