In applications we get to deal with a variety of data structures: trees, lists, arrays, queues, stacks, etc. Making sure that you choose the appropriate data structure for a particular problem is very important. If you choose right, you will ease the process of creating an application. If not – you risk unnecessarily increase complexity of your application, which in worst case, can lead to inability to maintain it further on. Along with proper data structures we need to choose proper design patterns to make sure that future extensibility and flexibility of usage are always available.
Imagine you have a list of some sort as your data structure. Let’s say in our example this list will represent a catalog of cars in a dealership. The list consists of items – cars. That’s pretty easy and straightforward – a no-brainer. What about enabling a list to contain a sub-list or a sub-catalog in our case? What if dealership wants to categorize all or part of items in their catalog? If we do not use proper approach from the beginning, we can face a trouble of making it flexible to extend just like that. The right approach in this situation will be to apply Composite Design Pattern.
The Composite Pattern
allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets client treat individual objects and compositions of objects uniformly.
Let’s dissect some of those aspects one by one:
- First of all Composite Pattern allows us to build structures of objects in the form of a tree.
- The tree can contain individual objects (leaves) as well as compositions of objects (sub-trees).
- We can treat any node, whether it is a leaf or a composite, uniformly, and apply the same operations over both of them.
Why is using the Composite Pattern a good approach in our case? It enables us to add to or extend the system at any point in time. Moreover, you can do that during runtime. The way the Composite Pattern works is by having an abstract class Component that has two different types of children: Leaf and Composite. Leaf is a Component with basic operation available. Composite is a Component that has operations to manipulate its structure, like addComponent(), removeComponent(), and getChild(int). It can also has other operations defined in the class. Client uses the Component class, thus treating both leaves and composites in the same way.
One note to make here is that Component class will need to define operations to manipulate children for composites. That means leaves will also inherit this behavior. We can’t really define a proper default implementation for each one of those. In cases like that the best things to do might be to throw a runtime exception as a default implementation.
Since Component class has operations for leaf nodes and for composite nodes it makes it violate the Single Responsibility Principle. We no longer have a one single reason to change the class. However, in this case this is done sort of intentionally. One thing to understand about OOP principles and patterns is that they are great guidelines, and not laws. You often do not need to tear your hair up in the pursuit of satisfying all of the principles’ teachings to make sure that you have the most OOP design ever. In simple cases, you might not need to adhere by any of them at all. Just KISS – Keep It Simple Stupid! In our case here the trade-off has been made in the desire for transparency: allowing Component class to contain operations for both types of children, a client can treat both of them uniformly. What it comes down to is the choice, whether to violate principles or not, should be decided depending on the context of the development: it might be perfectly fine to do that in our case, while other times we might need to be a little more cautious.
// Graphic Composite Example in Java
// Source: https://en.wikipedia.org/wiki/Composite_pattern
import java.util.ArrayList;
/** "Component" */
interface Graphic {
//Prints the graphic.
public void print();
}
/** "Composite" */
class CompositeGraphic implements Graphic {
//Collection of child graphics.
private final ArrayList<Graphic> childGraphics = new ArrayList<>();
//Adds the graphic to the composition.
public void add(Graphic graphic) {
childGraphics.add(graphic);
}
//Prints the graphic.
@Override
public void print() {
for (Graphic graphic : childGraphics) {
graphic.print(); //Delegation
}
}
}
/** "Leaf" */
class Ellipse implements Graphic {
//Prints the graphic.
@Override
public void print() {
System.out.println("Ellipse");
}
}
/** Client */
public class CompositeDemo {
public static void main(String[] args) {
//Initialize four ellipses
Ellipse ellipse1 = new Ellipse();
Ellipse ellipse2 = new Ellipse();
Ellipse ellipse3 = new Ellipse();
Ellipse ellipse4 = new Ellipse();
//Creates two composites containing the ellipses
CompositeGraphic graphic2 = new CompositeGraphic();
graphic2.add(ellipse1);
graphic2.add(ellipse2);
graphic2.add(ellipse3);
CompositeGraphic graphic3 = new CompositeGraphic();
graphic3.add(ellipse4);
//Create another graphics that contains two graphics
CompositeGraphic graphic1 = new CompositeGraphic();
graphic1.add(graphic2);
graphic1.add(graphic3);
//Prints the complete graphic (Four times the string "Ellipse").
graphic1.print();
}
}
Composite Design Pattern often can be paired up with the Iterator Pattern to go through all of the Composite’s items. If we have a function print() to call on one leaf or the whole composite, Iterator will help us with that.



One thought on “Composite Pattern”