Java design patterns: Composite

Today we will take a look at another Java design pattern from the category of structural patterns – Composite. Design patterns in this category deal with class structure such as inheritance and composition.

Read more – a series of design patterns:

What is the Composite Design Pattern?

Design pattern Composite is a design pattern that is used to create a tree structure of objects that allows the client to work with individual objects and their groupings (composites) in the same way because they have the same interface.

What problem does the Composite design pattern solve?

When working with a tree structure, programmers often have to distinguish between a leaf node and a branch. This makes the code more complex and therefore more error prone. The solution is an interface that allows complex and primitive objects to be handled uniformly.

The problem that Composite solves is the need to work with objects and their composites in a uniform way. This allows the client to recursively traverse and manipulate the entire tree structure, without the need to distinguish between individual objects and their groupings.

Example of Composite implementation in Java

Now we will show how to implement the Composite pattern in Java. We will create a complex tree structure composed of both individual objects (leaves) and compositions of objects (branches). By using the Composite pattern, we achieve uniform execution of operations on individual parts of the tree regardless of type.

The CastStromuinterface represents a common interface for both leaf nodes(Leaf) and composite nodes(Branch). This interface defines the vykonajOperaciu() method, which is key to this pattern.

CastStromu.java

package designpatterns;

// CastStromu - rozhranie pre všetky objekty v strome
public interface CastStromu {
    void vykonajOperaciu();
}

The List class represents the leaf component in the hierarchy. Implements the CastStrom interface and provides an implementation of the vykonajOperaciu() method. The leaf components have no child elements and perform operations.

List.java

package designpatterns;

// List - konkrétna časť stromu reprezentujúca list v strome
public class List implements CastStromu {
    private String id;

    public List(String id) {
        this.id = id;
    }

    public void vykonajOperaciu() {
        System.out.println("Vykonávam operáciu na liste s názvom: " + id);
    }
}

The Branch class represents a composite component in the hierarchy. It also implements the CastStromu interface and maintains a collection(castiStromu) of child components, which can be either leaf components or other composite components.

The pridajCast() method allows you to add child components and thus construct a hierarchical structure.

Branch.java

package designpatterns;

import java.util.ArrayList;

// Konkrétna časť stromu reprezentujúca vetvu v strome
public class Vetva implements CastStromu {
    private String id;
    private java.util.List<CastStromu> castiStromu = new ArrayList<>();

    public Vetva(String id) {
        this.id = id;
    }

    public void pridajCast(CastStromu cast) {
        castiStromu.add(cast);
    }

    public void vykonajOperaciu() {
        System.out.println("Vykonávam operáciu na vetve s názvom: " + id);
        for (CastStromu cast : castiStromu) {
            cast.vykonajOperaciu();
        }
    }
}

In the main method, an instance of the root composite component (kmenovaVetva) is created and child components (both leaf and composite) are added to it.

When the vykonajOperaciu() method is called on a trunk component ((kmenovaVetva), it recursively calls the vykonajOperaciu() method on all child components, resulting in operations being performed on the entire structure.

Main.java

import designpatterns.*;

public class Main {
    public static void main(String[] args) {
        // Vytvorí strom z listov a vetiev
        System.out.println("Skladám strom z kmeňa, listov a vetiev.");
        List list1 = new List("List 1");
        List list2 = new List("List 2");
        List list3 = new List("List 3");

        Vetva vetva1 = new Vetva("Vetva 1");
        vetva1.pridajCast(list1);
        vetva1.pridajCast(list2);

        Vetva vetva2 = new Vetva("Vetva 2");
        vetva2.pridajCast(list3);

        Vetva kmenovaVetva = new Vetva("Kmeň");
        kmenovaVetva.pridajCast(vetva1);
        kmenovaVetva.pridajCast(vetva2);

        // Vykonáva operáciu na celej stromovej štruktúre
        kmenovaVetva.vykonajOperaciu();
    }
}

The output of this example is:

Java code output from the Composite example
code output

Java Composite Summary

The Composite design pattern is used in situations where it is necessary to work uniformly with individual objects and their groupings.

We have prepared the files with the above example in the form of code that you can run directly in Java. Download the Java Composite code here.

About the author

Jozef Wagner

Java Developer Senior

I have been programming in Java for more than 10 years, currently I am working in msg life Slovakia as a senior Java programmer and I help customers to implement their requirements into Life Factory insurance software. In my free time I like to relax in the mountains or play a good computer game.

Let us know about you