C++ Composite Pattern Example

1. Definition

The Composite Design Pattern allows you to treat individual objects and compositions of objects uniformly. It creates a tree structure of simple and composite objects.

2. Problem Statement

How can we build a structure of objects where individual objects and groups of objects are treated the same way? Directly managing the collection of these objects can become complex, especially if they have different operations and behaviors.

3. Solution

With the Composite pattern, you abstract both single elements and composites (collections of elements) behind a shared interface. This way, clients can treat both uniformly.

4. Real-World Use Cases

1. Graphics systems where shapes can be composed of other shapes (e.g., drawing a picture using circles, rectangles, and groups of shapes).

2. Organizational structures (e.g., a company hierarchy with employees and departments).

3. File and directory systems.

5. Implementation Steps

1. Define a common interface for both simple and composite objects.

2. Create concrete classes for simple objects and another class that represents composite objects.

3. The composite class should contain a collection of the common interface.

4. Client code treats both simple and composite objects uniformly through the common interface.

6. Implementation in C++

// Common interface for both simple and composite objects
class Component {
public:
    virtual ~Component() {}
    virtual void operation() const = 0;
};
// Leaf (simple object)
class Leaf : public Component {
public:
    void operation() const {
        std::cout << "Leaf operation\n";
    }
};
// Composite
class Composite : public Component {
private:
    std::vector<Component*> children;
public:
    void add(Component* component) {
        children.push_back(component);
    }
    void operation() const {
        std::cout << "Composite operation:\n";
        for (Component* child : children) {
            child->operation();
        }
    }
};
int main() {
    // Create simple objects
    Leaf* leaf1 = new Leaf();
    Leaf* leaf2 = new Leaf();
    // Create composite
    Composite* composite = new Composite();
    composite->add(leaf1);
    composite->add(leaf2);
    // Execute operation
    composite->operation();
    delete leaf1;
    delete leaf2;
    delete composite;
    return 0;
}

Output:

Composite operation:
Leaf operation
Leaf operation

Explanation:

1. Component is the common interface for both simple and composite objects.

2. Leaf represents simple objects and implements the Component interface.

3. Composite is an object that can have zero or more children (either Leaf or even other Composite objects) and also implements the Component interface.

4. In the main function, two Leaf objects and one Composite object are created. The Leaf objects are added to the Composite, and when the operation is executed on the Composite, it in turn calls the operation on each child.

7. When to use?

Use the Composite pattern when:

1. You want to represent a part-whole hierarchy in your system.

2. You want clients to treat individual objects and compositions uniformly.

3. The structure might be recursive, with components having other components as children.


Comments