Project Technical Lead
Java design pattern: 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:
- Design pattern Singleton
- Design pattern Builder
- Design pattern Prototype
- Design pattern Factory
- Design pattern Abstract factory
- Design pattern Bridge
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 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.