Composite Design Pattern in Ruby

1. Definition

The Composite Design Pattern lets clients treat individual objects and compositions of objects uniformly. In other words, it allows you to compose objects into tree structures to represent part-whole hierarchies.

2. Problem Statement

Suppose you have individual objects and also compositions of these objects. It becomes a challenge to treat them in the same way, especially when operations and behaviors need to be applied.

3. Solution

The Composite pattern suggests introducing a shared interface for both simple and complex objects. If we see a system as a tree structure, leaf nodes in the tree can represent simple objects while branches or nodes can represent complex objects (compositions).

4. Real-World Use Cases

1. Graphic systems where shapes can be made up of simpler shapes.

2. Organizational structures (e.g., a company's departments and employees).

3. File and directory systems.

5. Implementation Steps

1. Define a Component interface with methods that make sense for both simple and complex components.

2. Create Leaf classes that implement the Component interface.

3. Create Composite classes that also implement the Component interface, and can store child components.

4. Client code can treat both Leaf and Composite objects uniformly.

6. Implementation in Ruby

# Step 1: Component Interface
module GraphicComponent
  def draw
    raise NotImplementedError, 'Subclasses must define `draw`.'
  end
  def add(component)
  end
  def remove(component)
  end
end
# Step 2: Leaf Classes
class Dot
  include GraphicComponent
  def draw
    "Drawing a dot."
  end
end
class Line
  include GraphicComponent
  def draw
    "Drawing a line."
  end
end
# Step 3: Composite Class
class CompoundGraphic
  include GraphicComponent
  def initialize
    @children = []
  end
  def draw
    @children.map(&:draw).join("\n")
  end
  def add(component)
    @children << component
  end
  def remove(component)
    @children.delete(component)
  end
end
# Step 4: Client Code
compound_graphic = CompoundGraphic.new
compound_graphic.add(Dot.new)
compound_graphic.add(Line.new)
puts compound_graphic.draw

Output:

Drawing a dot.
Drawing a line.

Explanation:

1. GraphicComponent serves as the common interface for both simple (Leaf) and complex (Composite) objects.

2. Dot and Line are leaf nodes. They don't have children and they implement the draw method.

3. CompoundGraphic is the composite node. It can have children (both Leaf and Composite), and its drawing method delegates the drawing operation to its children.

4. The client code demonstrates how to create composite structures and treat single and composite objects uniformly.

7. When to use?

Use the Composite Design Pattern when:

1. You need to represent part-whole hierarchies of objects.

2. You want clients to be able to ignore the difference between individual objects and compositions.

3. You want a unified interface for individual and composed objects.


Comments