Composite Design Pattern in Swift

1. Definition

The Composite design pattern is a structural pattern that allows you to compose objects into tree structures to represent part-whole hierarchies. The pattern lets clients treat individual objects and composites (compositions of objects) uniformly.

2. Problem Statement

Imagine you are designing a system for graphic elements. Each graphic element can be either a primitive shape (e.g., a circle or a square) or a composite of multiple shapes. How can you ensure that both primitive and composite shapes can be treated uniformly, especially when it comes to operations like rendering?

3. Solution

The solution is to have both primitive and composite objects implement the same interface. The composite object also maintains a list of children, which can be either primitive or composite objects. This way, operations can be delegated recursively down the object tree.

4. Real-World Use Cases

1. Graphic systems where shapes can be combined into more complex shapes.

2. Organizational structures where an employee can be a manager overseeing other employees.

3. File and directory systems.

5. Implementation Steps

1. Define a component interface with methods that make sense for both simple and complex objects.

2. Create leaf objects that implement the component interface.

3. Create composite objects that also implement the component interface and maintain a collection of child components.

6. Implementation in Swift Programming

// Step 1: Component interface
protocol Graphic {
    func draw()
}
// Step 2: Leaf objects
class Circle: Graphic {
    func draw() {
        print("Drawing a circle")
    }
}
class Square: Graphic {
    func draw() {
        print("Drawing a square")
    }
}
// Step 3: Composite object
class CompositeGraphic: Graphic {
    private var graphics: [Graphic] = []
    func add(_ graphic: Graphic) {
        graphics.append(graphic)
    }
    func draw() {
        for graphic in graphics {
            graphic.draw()
        }
    }
}
// Usage:
let circle1 = Circle()
let circle2 = Circle()
let square = Square()
let compositeGraphic = CompositeGraphic()
compositeGraphic.add(circle1)
compositeGraphic.add(circle2)
compositeGraphic.add(square)
compositeGraphic.draw()

Output:

Drawing a circle
Drawing a circle
Drawing a square

Explanation:

1. We started with a Graphic protocol, which is our component interface.

2. Both Circle and Square are leaf objects implementing the Graphic interface, making them indivisible building blocks.

3. The CompositeGraphic is also implementing the Graphic interface but contains a list of Graphic objects. When its draw method is called, it recursively calls the draw method on its children.

4. In our usage, we added two circles and a square to the CompositeGraphic and then drew it, resulting in each shape being drawn in sequence.

7. When to use?

Use the Composite pattern when:

1. You want to represent part-whole hierarchies.

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

3. You have a clear hierarchy or tree structure, and you want to simplify the client code by handling objects in this hierarchy uniformly.


Comments