Skip to content

Composite Pattern

Category: Structural Pattern

Overview

Compose objects into tree structures to represent part-whole hierarchies. This pattern lets clients treat individual objects and compositions of objects uniformly, enabling building of complex structures from simple components.

Usage Guidelines

Use when:

  • Need to represent part-whole hierarchies
  • Want to treat individual and composite objects uniformly
  • Objects can contain other objects of same type
  • Working with hierarchical data structures

Avoid when:

  • Structure is flat, not hierarchical
  • Leaves and composites require very different operations
  • Need strong type distinctions between components
  • Tree traversal overhead is unacceptable for performance

Implementation

class Shape:
    """Base class for all shapes."""

    def draw(self) -> str:
        """Draws the shape.

        Raises:
            NotImplementedError: Subclasses must implement this method.
        """
        raise NotImplementedError("Subclasses must implement this method.")

class Circle(Shape):
    """Represents a circle shape."""

    def draw(self) -> str:
        return "Drawing a circle."

class Rectangle(Shape):
    """Represents a rectangle shape."""

    def draw(self) -> str:
        return "Drawing a rectangle."

class CompositeShape(Shape):
    """A composite shape that can contain other shapes."""

    def __init__(self):
        """Initializes a CompositeShape with an empty list of shapes."""
        self.shapes = []

    def add(self, shape: Shape) -> None:
        """Adds a shape to the composite shape.

        Args:
            shape: The shape to be added.
        """
        self.shapes.append(shape)

    def remove(self, shape: Shape) -> None:
        """Removes a shape from the composite shape.

        Args:
            shape: The shape to be removed.
        """
        if shape in self.shapes:
            self.shapes.remove(shape)

    def draw(self) -> str:
        """Draws all shapes in the composite shape.

        Returns:
            str: A string representation of all drawn shapes.
        """
        return "Composite Shape: " + ", ".join(shape.draw() for shape in self.shapes)

Usage

# Create individual shapes
circle = Circle()
rectangle = Rectangle()

# Create composite
composite = CompositeShape()
composite.add(circle)
composite.add(rectangle)

# Draw individual shape
print(circle.draw())  # Drawing a circle.

# Draw composite (draws all contained shapes)
print(composite.draw())  # Composite Shape: Drawing a circle., Drawing a rectangle.

# Create nested composite
main_composite = CompositeShape()
main_composite.add(Circle())
main_composite.add(composite)

# Draw nested structure
print(main_composite.draw())
# Composite Shape: Drawing a circle., Composite Shape: Drawing a circle., Drawing a rectangle.

Trade-offs

Benefits:

  1. Clients treat simple and complex objects uniformly
  2. Easy to create complex tree structures through recursive composition
  3. Easy to add new component types (Open/Closed Principle)
  4. Simplified client code that doesn't distinguish between types

Drawbacks:

  1. Makes design overly general
  2. Hard to restrict component types for type safety
  3. Leaf-specific operations complicate interface
  4. Traversing deep trees can be slow

Real-World Examples

  • File systems with files and directories
  • GUI components with windows, panels, buttons
  • Graphics scenes with shapes, groups, scenes
  • Menu systems with menu items and submenus
  • Iterator
  • Visitor
  • Decorator
  • Flyweight

API Reference

design_patterns.structural.composite

Composite Pattern Example

This module demonstrates the Composite design pattern in Python using a base class Shape, along with concrete implementations Circle and Rectangle. The CompositeShape class allows for grouping of multiple shapes, enabling a unified interface to draw them collectively.

Example
circle = Circle()
rectangle = Rectangle()
composite = CompositeShape()
composite.add(circle)
composite.add(rectangle)
print(composite.draw())  # Output: Composite Shape: Drawing a circle., Drawing a rectangle.

Circle

Bases: Shape

Represents a circle shape.

Source code in src/design_patterns/structural/composite.py
class Circle(Shape):
    """Represents a circle shape."""

    def draw(self) -> str:
        return "Drawing a circle."

CompositeShape

Bases: Shape

A composite shape that can contain other shapes.

Source code in src/design_patterns/structural/composite.py
class CompositeShape(Shape):
    """A composite shape that can contain other shapes."""

    def __init__(self):
        """Initializes a CompositeShape with an empty list of shapes."""
        self.shapes = []

    def add(self, shape: Shape) -> None:
        """Adds a shape to the composite shape.

        Args:
            shape (Shape): The shape to be added.
        """
        self.shapes.append(shape)

    def draw(self) -> str:
        """Draws all shapes in the composite shape.

        Returns:
            str: A string representation of all drawn shapes.
        """
        return "Composite Shape: " + ", ".join(shape.draw() for shape in self.shapes)

__init__()

Initializes a CompositeShape with an empty list of shapes.

Source code in src/design_patterns/structural/composite.py
def __init__(self):
    """Initializes a CompositeShape with an empty list of shapes."""
    self.shapes = []

add(shape)

Adds a shape to the composite shape.

Parameters:

Name Type Description Default
shape Shape

The shape to be added.

required
Source code in src/design_patterns/structural/composite.py
def add(self, shape: Shape) -> None:
    """Adds a shape to the composite shape.

    Args:
        shape (Shape): The shape to be added.
    """
    self.shapes.append(shape)

draw()

Draws all shapes in the composite shape.

Returns:

Name Type Description
str str

A string representation of all drawn shapes.

Source code in src/design_patterns/structural/composite.py
def draw(self) -> str:
    """Draws all shapes in the composite shape.

    Returns:
        str: A string representation of all drawn shapes.
    """
    return "Composite Shape: " + ", ".join(shape.draw() for shape in self.shapes)

Rectangle

Bases: Shape

Represents a rectangle shape.

Source code in src/design_patterns/structural/composite.py
class Rectangle(Shape):
    """Represents a rectangle shape."""

    def draw(self) -> str:
        return "Drawing a rectangle."

Shape

Base class for all shapes.

Source code in src/design_patterns/structural/composite.py
class Shape:
    """Base class for all shapes."""

    def draw(self) -> str:
        """Draws the shape.

        Raises:
            NotImplementedError: Subclasses must implement this method.
        """
        raise NotImplementedError("Subclasses must implement this method.")

draw()

Draws the shape.

Raises:

Type Description
NotImplementedError

Subclasses must implement this method.

Source code in src/design_patterns/structural/composite.py
def draw(self) -> str:
    """Draws the shape.

    Raises:
        NotImplementedError: Subclasses must implement this method.
    """
    raise NotImplementedError("Subclasses must implement this method.")