Book cover

Buy on Leanpub (pdf and epub)

To report errors or typos, use this form.

Home | Dark Mode | Cite

Software Engineering: A Modern Approach

Marco Tulio Valente

1 Composite Design Pattern

1.1 Introduction

In this article, we will introduce the Composite design pattern, which, despite its importance, was not covered in Chapter 6 of the book.

We will use the same structure as the design patterns we explored in that chapter. Specifically, we’ll present the pattern by first describing a context, then a problem faced in this context, and finally, the solution proposed by the Composite design pattern.

1.2 Context

Let’s consider that we are implementing a graphic editor, similar to Paint (for Windows) or Canva (web-based). In this editor, we have interfaces and classes like the following:

interface Shape {
  void draw();
  void changeBackgroundColor(Color color); 
}

class Circle implements Shape {
  ... 
}

class Triangle implements Shape {
  ...
}

1.3 Problem

In our editor, we want to implement a feature that allows grouping shapes and then treating the resulting group as a single element.

For example, a composite shape may consist of a circle and two triangles (see the illustration). Once this grouping is created, we should be able to perform an operation on it, such as changing the background color of all three shapes, using a single method call.

Simple and composite shapes

This approach enables clients to work with simple or composite shapes in the same way, using the Shape interface. The client code doesn’t need to know whether it is manipulating a simple or composite shape.

1.4 Solution

The Composite design pattern is the solution to the problem we described. It allows us to organize composite objects in a tree-like structure and then lets clients handle these objects as if they were individual objects.

The main class of the pattern is responsible for the composite behavior. In our example, we implement a class like the following:

class CompositeShape implements Shape {

  private ArrayList<Shape> shapes = new ArrayList<Shape>();
  // list that stores the grouped shapes
  
  public void add(Shape shape) {
    shapes.add(shape);
  }

  public void remove(Shape shape) {
    shapes.remove(shape);
  }

  public void draw() {
    for (Shape shape: shapes) {
      shape.draw();   
    }
  }

  public void changeBackgroundColor(Color color) {
    for (Shape shape: shapes) {
      shape.changeBackgroundColor(color);   
    }
  }
}

Two main aspects about CompositeShape should be highlighted:

Let’s also examine a UML diagram with the classes we’ve created so far:

Classes for implementing the Composite Pattern

As a result, client code doesn’t need to distinguish between a simple shape and a composite shape, as in the following example:

class Client {
  void foo(Shape shape) {
    ...
    shape.draw(); // draws simple or composite shapes
    ...
  } 
}

class Main {
  public static void main(String[] args) {
    Client client = new Client();

    Circle c1 = new Circle();
    client.foo(c1);  // calls foo with a simple shape

    Triangle t1 = new Triangle();
    Triangle t2 = new Triangle();
    
    CompositeShape composite1 = new CompositeShape();
    composite1.add(c1);
    composite1.add(t1);
    composite1.add(t2);
    client.foo(composite1); // calls foo with a composite shape
  } 
}

Finally, note that we can create a hierarchical structure, organized as a tree, with composite shapes containing other composite shapes, as illustrated in this example:

CompositeShape composite2 = new CompositeShape();
composite2.add(composite1); // composite shape containing another composite shape

1.5 Conclusion

We should use the Composite pattern when we need to represent both simple and composite objects (groups of simple objects) uniformly. This approach simplifies client code that handles such objects, as it remains unaware of whether it is manipulating a simple or a composite object.

Exercises

1. There are three types of classes or interfaces in the Composite pattern:

Consider another example of the Composite pattern. Then answer:

  1. What is the client-visible interface? What methods does it define?
  2. What are the simple object classes? List their names.
  3. What is the composite class? List its name.

Check out the other articles on our site.