Skip to content

Composite Pattern

Composite is a structural design pattern that lets you compose objects into tree structures to represent whole-part hierarchies. We will explore it using a drawing application!

Note

This reading is based on the ShapeDrawingApp.

Problem

We want our drawing editor to support grouping and ungrouping operations so that a number of shapes can be collected together and treated as a single entity.

Solution 1

We could add a group member field into AbstractShape to indicate which group each shape belongs to (using the number -1 to indicate that the object is not in any group).

Pros/Cons
  • Pros: simple
  • Cons: cannot support nested groups

Solution 2

Introduce a new class ShapeGroup to manage a group of shapes.

Pros/Cons
  • Pros: Supports nested groups
  • Cons: You would have to treat primitives (shapes) and container classes (shapeGroups) differently, making the application more complex than necessary.

Solution 3

Implement the ShapeGroup class as a subclass of Shape.

The ShapeGroup class provides a means by which several shapes can be grouped together into a single entity which behaves in the same way as a single shape.

Note

Download the updated ShapeDrawingApp with a working grouping/ungrouping feature.

The Pattern

The composite design pattern provides a means of grouping together several objects of type T such that the grouped object is also of type T.

In the diagram above

  • Component:
    • Declares the abstraction for objects in the composition.
    • Could optionally implement default behavior common to all subclasses.
  • Composite:
    • Represents an aggregate object (collection of components).
    • Usually has methods to add/remove component.
    • Typically implements operations of Component simply by calling the same operation for each of its constituent components.
  • Concrete Component (a.k.a Leaf)
    • Defines behavior for primitive objects in the composition.

When to use this pattern?

  • Need to manipulate a hierarchical collection of "primitive" and "aggregate" objects.
  • Need to process (treat) primitive objects the same way as aggregate objects.
  • Examples: file/folder hierarchies, organizational charts, tables of contents, parsing structured documents like XML/HTML, etc.

Advantage

Clients typically only need to interact with the Component interface; they would be unaware if they're dealing with a composite or a leaf object. This hides the complexity of the data structure from the outside world.