定义

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构,并且能像使用单独对象一样使用组合对象。组合模式让客户端代码对单个对象和复合对象的使用具有一致性。

在组合模式中,我们定义以下几个角色:

  1. Component:这是一个抽象组件接口,它定义了所有组件共有的行为。这些行为包括添加和删除子组件、显示子组件等。

  2. Leaf:这是具体组件,也就是叶子节点,它实现了组件接口,但没有子组件。

  3. Composite:这也是具体组件,但它充当容器角色,它持有一组子组件,并实现了组件接口。Composite 负责在其内部实现子组件的递归组合。

示例

下面是一个C++中使用组合模式的示例,我们创建一个简单的图形系统,其中包含圆形、矩形和组合图形(可以包含其他图形):

#include <iostream>  
#include <vector>  
  
// 组件接口  
class Shape {  
public:  
    virtual void draw() = 0;  
    virtual void add(Shape* shape) = 0;  
    virtual void remove(Shape* shape) = 0;  
    virtual std::vector<Shape*> getChildren() = 0;  
};  
  
// 叶子节点:圆形  
class Circle : public Shape {  
private:  
    int radius;  
  
public:  
    Circle(int radius) : radius(radius) {}  
  
    void draw() override {  
        std::cout << "Drawing Circle with radius: " << radius << std::endl;  
    }  
  
    void add(Shape* shape) override {  
        // 圆形不能添加子组件  
    }  
  
    void remove(Shape* shape) override {  
        // 圆形不能删除子组件  
    }  
  
    std::vector<Shape*> getChildren() override {  
        return {}; // 圆形没有子组件  
    }  
};  
  
// 叶子节点:矩形  
class Rectangle : public Shape {  
private:  
    int width, height;  
  
public:  
    Rectangle(int width, int height) : width(width), height(height) {}  
  
    void draw() override {  
        std::cout << "Drawing Rectangle with width: " << width << " and height: " << height << std::endl;  
    }  
  
    void add(Shape* shape) override {  
        // 矩形不能添加子组件  
    }  
  
    void remove(Shape* shape) override {  
        // 矩形不能删除子组件  
    }  
  
    std::vector<Shape*> getChildren() override {  
        return {}; // 矩形没有子组件  
    }  
};  
  
// 容器节点:组合图形  
class CompositeShape : public Shape {  
private:  
    std::vector<Shape*> children;  
  
public:  
    void draw() override {  
        std::cout << "Drawing CompositeShape:" << std::endl;  
        for (Shape* child : children) {  
            child->draw();  
        }  
    }  
  
    void add(Shape* shape) override {  
        children.push_back(shape);  
    }  
  
    void remove(Shape* shape) override {  
        auto it = std::find(children.begin(), children.end(), shape);  
        if (it != children.end()) {  
            children.erase(it);  
        }  
    }  
  
    std::vector<Shape*> getChildren() override {  
        return children;  
    }  
};  
  
int main() {  
    // 创建组合图形  
    CompositeShape composite;  
  
    // 添加子组件  
    composite.add(new Circle(5));  
    composite.add(new Rectangle(10, 20));  
  
    // 创建另一个组合图形,并添加到之前的组合图形中  
    CompositeShape anotherComposite;  
    anotherComposite.add(new Circle(10));  
    anotherComposite.add(new Rectangle(5, 15));  
    composite.add(&anotherComposite); // 注意这里传递的是指针  
  
    // 绘制组合图形  
    composite.draw();  
  
    // 清理资源(在实际应用中,你可能需要更复杂的内存管理策略)  
    for (Shape* child : composite.getChildren()) {  
        delete child;  
    }  
  
    return 0;  
}

在这个示例中,Shape 是一个抽象组件接口,它定义了所有组件共有的行为。Circle 和 Rectangle 是具体组件(叶子节点),它们实现了 Shape 接口但没有子组件。CompositeShape 是容器组件,它持有一组子组件,并实现了 Shape 接口。

在组合模式示例中,CompositeShape 类扮演着组合对象的角色,它管理着一组子组件,并提供了添加、删除和绘制子组件的方法。由于 CompositeShape 也实现了 Shape 接口,它可以在其他组合对象中被当作一个普通的组件来使用,从而实现了对象组合的一致性。

组合模式的主要优点有:

  1. 一致性:客户端代码可以以一致的方式来处理单个对象和组合对象,无需关心对象是否是复合的。

  2. 扩展性:你可以方便地添加新的组件类型,因为系统是基于接口而不是具体类构建的。

  3. 灵活性:你可以很容易地组合和分解对象,因为组件之间的层次结构是动态的。

  4. 封装性:组合模式允许你将一些对象组合成一个树形结构来表现“部分-整体”的层次结构,并且能像使用单个对象一样使用组合对象。

然而,组合模式也有一些潜在的缺点:

  1. 复杂性:组合模式可能会增加系统的复杂性,特别是当组合对象嵌套层次很深时。

  2. 内存使用:如果组合对象包含大量子组件,可能会占用较多的内存。

  3. 性能开销:在遍历组合对象以执行某些操作时,可能会产生额外的性能开销。

在实际应用中,组合模式常用于实现如文件/目录结构、UI组件树、编译器中的语法树等场景,其中整体和部分可以以同样的方式被对待。

回到示例代码,在 main 函数中,我们创建了一个 CompositeShape 对象 composite,并向其添加了几个子组件,包括 Circle 和 Rectangle 对象,以及另一个 CompositeShape 对象 anotherComposite。然后我们调用 draw 方法来绘制整个组合对象。在绘制过程中,CompositeShape 会递归地调用其所有子组件的 draw 方法,从而实现了组合对象的绘制。

最后,我们需要注意资源管理问题。在这个示例中,我们手动删除了所有动态分配的对象,以避免内存泄漏。在实际应用中,你可能需要采取更复杂的内存管理策略,例如使用智能指针(如 std::shared_ptr 或 std::unique_ptr)来自动管理对象的生命周期。

02-29 12:54