Learn the Composite Design Pattern
This is the 8th post in a series on design patterns.
Composite is a structural design pattern that lets you compose objects into tree structures and then work with these structures as if they were individual objects.
Composite Design Pattern
Using the Composite pattern only makes sense when the core model of your application can be represented as a tree.
Assume that you have two types of objects: File and Folder. It is possible for a folder to contain a number of files as well as folders. These subfolders can also contain files, folders, and so on.
Let’s say you decide to create a disk management system that uses these classes. Suppose you want to calculate the total storage space occupied by the folder that has many sub-folders and files.
What is the best method for determining the total size of such a folder?
The direct approach would be to go inside the folder, look at all the files, and calculate the total size.
In Composite patterns, you work with folders and files through a common interface that declares a way to calculate the total size.
How does this work?
In the case of a file, it would simply return the file’s size. A folder’s total size would be calculated by looking at every file in the folder and asking what size it is. This process would continue until all files had been calculated.
One benefit of this approach is that you don’t have to worry about the concrete classes of objects that make up the tree. It doesn’t matter whether an object is a file or a folder. Using the common interface, you can treat them all the same. The objects themselves pass the request down the tree when they call a method.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
Implementation steps
- Ensure that the core model of your application can be represented as a tree structure. Break it down into simple elements and containers.
- The Component interface should include a list of methods that are appropriate for both simple and complex components.
- Create a Leaf class that represents simple elements. There may be multiple Leaf classes in a program.
- To represent complex elements, create a Composite (or Container) class. There should be a field for storing references to subelements in this class. In order to store both leaves and composite, the array must be declared with the Component interface type.
- Define the methods for adding and removing child elements from the container.
Source Code Implementation
The Storage (Component) interface describes operations that are common to both file (simple) and folder (complex) elements.
A File(Leaf) class is a basic element of a tree that does not have sub-elements. Since the leaf component has no one to delegate the work to, it ends up doing most of the real work.
A Folder (Composite) class is an element that has sub-elements files and folders. A folder does not know the concrete class of its children. All sub-elements are controlled by the Storage(Component) interface. In response to a request, a folder class delegated the work to its sub-elements, which then processed intermediate results and returned the final result to the client.
The CompositeClient class interacts with all elements via the Storage interface. Therefore, the client can work with both files and folders in the same way.
// Output
File File1-levelTwo, size: 924
File File2-levelTwo, size: 245
File File3-levelTwo, size: 226
>>> Folder levelTwo, size: 1395
>>> Folder levelOne1st, size: 1395
File File1-levelOne2nd, size: 6
File File2-levelOne2nd, size: 432
>>> Folder levelOne2nd, size: 438
File File1-folderRoot, size: 537
>>> Folder folderRoot, size: 2370
-------------------------------------
Folder folderRoot, total size: 2370
-------------------------------------
When To Apply Composite Design Pattern
- When you need to implement a tree-like object structure, use the Composite pattern. With the Composite pattern, you get two basic element types that share a common interface: simple leaves and complex containers/composite. Containers can be composed of leaves as well as other containers. With this, you can construct a nested recursive object structure similar to a tree.
- When you want the client code to treat both simple and complex elements the same way, use this pattern.
Pros of Composite Design Pattern
- Using polymorphism and recursion, you can work with complex tree structures more conveniently.
- With the Open/Closed concept, you can introduce new elements to the app without breaking the existing code, which now works with the object tree.