1. Introduction

In this quick tutorial, we’ll introduce the composite design pattern in Java.

We’re going to describe the structure and the purpose of its use.

2. The Structure

The composite pattern is meant to allow treating individual objects and compositions of objects, or “composites” in the same way.

It can be viewed as a tree structure made up of types that inherit a base type, and it can represent a single part or a whole hierarchy of objects.

We can break the pattern down into:

  • component – is the base interface for all the objects in the composition. It should be either an interface or an abstract class with the common methods to manage the child composites.
  • leaf – implements the default behavior of the base component. It doesn’t contain a reference to the other objects.
  • composite – has leaf elements. It implements the base component methods and defines the child-related operations.
  • client – has access to the composition elements by using the base component object.

3. Practical Example

Now, let’s dive into the implementation. Let’s suppose we want to build a hierarchical structure of departments in a company.

3.1. The Base Component

As a component object we’ll define a simple Department interface:

public interface Department {
    void printDepartmentName();
}

3.2. Leafs

For the leaf components, let’s define classes for financial and sales departments:

public class FinancialDepartment implements Department {

    private Integer id;
    private String name;

    public void printDepartmentName() {
        System.out.println(getClass().getSimpleName());
    }

    // standard constructor, getters, setters
}

The second leaf class, SalesDepartment, is similar:

public class SalesDepartment implements Department {

    private Integer id;
    private String name;

    public void printDepartmentName() {
        System.out.println(getClass().getSimpleName());
    }

    // standard constructor, getters, setters
}

Both classes implement the printDepartmentName() method from the base component, where they print the class names for each of them.

Also, as they’re leaf classes, they don’t contain other Department objects.

Next, let’s see a composite class as well.

3.3. The Composite Element

As a composite class, let’s create a HeadDepartment class:

public class HeadDepartment implements Department {
    private Integer id;
    private String name;

    private List<Department> childDepartments;

    public HeadDepartment(Integer id, String name) {
        this.id = id;
        this.name = name;
        this.childDepartments = new ArrayList<>();
    }

    public void printDepartmentName() {
        childDepartments.forEach(Department::printDepartmentName);
    }

    public void addDepartment(Department department) {
        childDepartments.add(department);
    }

    public void removeDepartment(Department department) {
        childDepartments.remove(department);
    }
}

This is a composite class as it holds a collection of Department components, as well as methods for adding and removing elements from the list.

The composite printDepartmentName() method is implemented by iterating over the list of leaf elements and invoking the appropriate method for each one.

4. Testing

For testing purposes, let’s have a look at a CompositeDemo class:

public class CompositeDemo {
    public static void main(String args[]) {
        Department salesDepartment = new SalesDepartment(
          1, "Sales department");
        Department financialDepartment = new FinancialDepartment(
          2, "Financial department");

        HeadDepartment headDepartment = new HeadDepartment(
          3, "Head department");

        headDepartment.addDepartment(salesDepartment);
        headDepartment.addDepartment(financialDepartment);

        headDepartment.printDepartmentName();
    }
}

First, we create two instances for the financial and sales departments. After, we instantiate the head department and add up the previously created instances to it.

Finally, we can test the printDepartmentName() composition method. As we expect, the output contains the class names of each leaf component:

SalesDepartment
FinancialDepartment

5. Conclusion

In this article, we learned about the Composite design pattern. The write-up highlights the main structure and demonstrates the usage through the practical example.

As usual, the complete code is available in the Github project.