Composite Design Pattern

The Composite design pattern allows you to create objects from an interface by making them all similar and allowing them all to be treated in the same way.

What are the parts of the composite design pattern?

Let’s look at the parts of this composite pattern:

  • Interface Composite: is the abstract class or interface from which the classes are implemented.
  • Leaf: is the object that was implemented from the component.
  • Composition: component with children / leaves.
  • Client: is the class that handles the composite through the Component interface.

Think of this component as a tree structure in this way.

Notice that a composition can be composed of other compositions or simply of a single child or leaf. All, both compositions and sheets can be treated in the same way through their interface.

Composite Patter Tree Java

How to create the composite design pattern in Java

A simple example of common use to better understand this pattern is the typical example of Employees and Subordinates with their different roles within.

Here we have an example of a Composite Employee of which are created a Manager y Developer. One Manager handles several developers that depend on it.

Composite Example Java

Let’s define our interface from which we will then implement other components.

package patterns.composite;

import java.util.List;

public interface Employee {

    String getName();

    void add(Employee e);

    void remove(Employee e);

    List<Employee> getEmployees();

    int calculatePoints();

}

We now define the Manager component that implements the interface.

A Manager is a composite that has a list of developers in charge.

Both classes, Manager and Developer implement the interface Employee.

composite-design-pattern.png

package patterns.composite;

import java.util.ArrayList;
import java.util.List;

public class Manager implements Employee {

    private List<Employee> employees = new ArrayList<>();
    private String name;

    public Manager(String name) {
        this.name = name;
    }

    @Override
    public List<Employee> getEmployees() {
        return this.employees;
    }

    @Override
    public void add(Employee e) {
        if (e != null) {
            this.employees.add(e);
        }
    }

    @Override
    public void remove(Employee e) {
        if (e != null) {
            this.employees.remove(e);
        }
    }

    @Override
    public int calculatePoints() {
        if (this.employees.isEmpty()) {
            return 0;
        }
        return Math.round(this.employees.stream().mapToInt(e -> {
            System.out.println(e);
            return e.calculatePoints();
        }).sum());
    }

    @Override
    public String getName() {
        return this.name;
    }

}

Let’s define the Developer class that also implements the Employee interface.

Note that a manager can be composed of other employees, in this case developers.

A developer, we assume in this example, cannot have other employees in charge; so the methods to add items to the list have no functionality.

package patterns.composite;

import java.util.List;
import java.util.Random;

public class Developer implements Employee {

    String name;

    public Developer(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void add(Employee e) {
    // nothing to implement
    }

    @Override
    public void remove(Employee e) {
    // nothing to implement    
    }

    @Override
    public List<Employee> getEmployees() {
    // nothing to implement
     return null;
    }

    @Override
    public int calculatePoints() {
        return new Random().nextInt(24);
    }

    @Override
    public String toString() {
        return "I am " + getName() + ", Developer";
    }

}

Testing the composite with the client class.

We define the ClientComposite class that will make use of the Employee component.

package patterns.composite;

public class ClientComposite {

    public static void main(String... args) {

        Employee m1 = new Manager("Marc");

        Employee d1 = new Developer("Maria");
        Employee d2 = new Developer("Ema");
        Employee d3 = new Developer("Brian");

        m1.add(d1);
        m1.add(d2);
        m1.add(d3);

        Employee m2 = new Manager("Susan");

        Employee d4 = new Developer("James");
        Employee d5 = new Developer("Michael");

        m2.add(d4);
        m2.add(d5);

    }
}
I am Maria, Developer
I am Ema, Developer
I am Brian, Developer
19
I am James, Developer
I am Michael, Developer
20

Conclusion:

We saw that the composite pattern is useful to represent hierarchies and to unify the operations between objects and individual objects in order to treat them all in the same way simplifying their use.

The disadvantage of this composite pattern is that many times you must define general methods whose implementation will be empty in some cases, such the case of the developer class that we saw.

You can review this code GitHub

Hi! If you find my posts helpful, please support me by inviting me for a coffee :)

See also