Java Structural Design Patterns – Composite Pattern
Composite Pattern is considered as a Structural Design Pattern. This pattern describes a group of objects and treating them as an individual instance of the same type of object because all exhibiting similar functionality.
In this pattern, we compose objects and creates a tree structure and represents part-whole hierarchies. Each node in the tree performs some task. In object-oriented programming, this is known as a “has-a” relationship between objects.
If you find that in your application code you are using multiple objects in the same way, and often have nearly identical code to handle each of them, then Composite Pattern is a good choice
The pattern defines below concepts-
- Component – It is either an interface or an abstract class with the general methods to manage the child composites. It is the base interface.
- Leaf – it represents leaf object in composite design, it has no children (no reference to the other objects.) and defines behavior for primitive objects in the composite pattern.
- Composite – it stores children’s i.e leaf elements. It implements base component methods and defines child-related operations.
- Client – it manipulates objects in the composition through the component interface.
Composite Pattern by Example
For understanding, we will take a simple example of company hierarchy. In this, we will have a General Manager, Manager, and Developer. If we refer above diagram, in our example we will try to create something like below
Let us define the component-
1 2 3 |
public interface Employee { public void print(); } |
The component interface is very simple and defines a print method.
We will define 2 composites as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class Manager implements Employee { private String name; List<Employee> reportees = new ArrayList<>(); public Manager(String name) { this.name = name; } public void addReportee(Employee e) { reportees.add(e); } public void remove(Employee e) { reportees.remove(e); } @Override public void print() { System.out.println(name); System.out.println("Reportees.."); for(Employee e : reportees) { e.print(); } } } |
and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class GeneralManager implements Employee { private String name; List<Employee> reportees = new ArrayList<>(); public GeneralManager(String name) { this.name = name; } public void addReportee(Employee e) { reportees.add(e); } public void remove(Employee e) { reportees.remove(e); } @Override public void print() { System.out.println(name); System.out.println("Reportees.."); for(Employee e : reportees) { e.print(); } } } |
Both the above composites are similar and they have a way to store their leaf nodes or you can say direct reportees.
Now let us add leaf component-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Developer implements Employee { private String name; public Developer(String name) { this.name = name; } @Override public void print() { System.out.println(name); } } |
Running the example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class CompositeDemo { public static void main(String[] args) { Developer dev1 = new Developer("Developer 1"); Developer dev2 = new Developer("Developer 2"); Developer dev3 = new Developer("Developer 3"); Developer dev4 = new Developer("Developer 4"); Manager mgr1 = new Manager("Manager 1"); Manager mgr2 = new Manager("Manager 2"); GeneralManager gmgr = new GeneralManager("General Manager"); gmgr.addReportee(mgr1); gmgr.addReportee(mgr2); gmgr.addReportee(dev3); mgr1.addReportee(dev1); mgr1.addReportee(dev4); mgr2.addReportee(dev2); gmgr.print(); } } |
The main program acts as a client and creates a hierarchy. If we run the example we will get below output.
1 2 3 4 5 6 7 8 9 10 |
General Manager Reportees.. Manager 1 Reportees.. Developer 1 Developer 4 Manager 2 Reportees.. Developer 2 Developer 3 |
The program prints Composite and their Children.
Conclusion
As mentioned earlier when clients need to ignore the difference between compositions of objects and individual objects you can use this pattern. Also, if programmers find that they are using multiple objects in the same way, and often have nearly identical code to handle each of them, then the composite pattern is a good choice. The source code is available in our Github repository.
Download Code