This chapter covers the Composite pattern.
GoF Definition
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Concept
Consider a shop that sells different kinds of dry fruit, such as cashews, dates, and walnuts. Each of these items has a certain price. Let’s assume that you can purchase any of these individual items, or you can purchase “gift packs” (or boxed items), which are composed of different dry fruit items. In this case, the cost of a packet is the sum of its component parts. The Composite pattern is useful in a similar situation, in which you treat both the individual parts and the combination of the parts in the same way so that you can process them uniformly.
You can compose objects into a tree structure to represent a partwhole hierarchy.
You can access both the composite objects (branches) and the individual objects (leaf nodes) uniformly. As a result, you can reduce the complexity of the code and make the application less prone to errors.
Real-World Example
Apart from our previous example, you can also think of an organization that consists of many departments. In general, an organization has many employees. Some of these employees are grouped to form a department, and those departments can be further grouped to build the high-level structure of the organization.
Computer-World Example
I mentioned that a tree data structure could follow this concept where the clients can treat the leaves of the tree and the nonleaves (or branches of the tree) in the same way. So, when you see a hierarchical data, you can get a clue that the Composite pattern can be useful. XML files are very common examples with such tree structures.
When you traverse the tree, you often use the concept of an Iterator design pattern, which is covered in Chapter 18.
Implementation
Let’s also assume that at the end of the year, one lecturer from the CSE department submits his resignation. The following example considers all the scenarios mentioned.
Class Diagram
Solution Explorer View
Demonstration
From the associated comments, it’s easy to understand that these three properties set an employee’s name, their corresponding department, and the designation. The Employee and CompositeEmployee concrete classes implement this interface. Employee class (lecturers) acts as a leaf node, and the other one is a nonleaf node. One or more employees can report to a HOD. So, it is treated as a nonleaf (or branch) node. Similarly, all HODs report to the principal. So, Principal is another nonleaf node.
The mathematics lecturers are named M. Joy and M. Roony. The CSE teachers are named C. Sam, C. Jones, and C. Marium. These lecturers do not supervise anyone, so they are treated as leaf nodes.
The CompositeEmployee class maintains a list and two additional methods called AddEmployee(...) and RemoveEmployee(...). These methods add an employee to the list or remove an employee from the list.
Output
Q&A Session
11.1 What are the advantages of using the Composite design pattern?
In a tree-like structure, you can treat both the composite objects (branch nodes) and the individual objects (leaf nodes) uniformly. In this example, I used a common method called DisplayDetails to print both the composite object structure (the principal or department heads) and the single objects (the lecturers).
It is common to implement a part-whole hierarchy using this design pattern.
You can easily add a new component to the architecture or delete an existing component from the architecture.
11.2 What are the challenges associated with using the Composite design pattern?
If you want to maintain the ordering of child nodes (for example, if the parse trees are represented as components), you may need to take special care.
If you are dealing with immutable objects, you cannot delete them.
You can easily add a new component, but maintenance can be difficult over a period of time. Sometimes you may want to deal with a composite that has special components. This kind of constraint may cause additional costs to the development because you may need to implement a dynamic checking mechanism to support the concept.
11.3 In this example, you used a list data structure . Are other data structures OK to use?
Absolutely. There is no universal rule. You are free to use your preferred data structure. The GoF also confirmed that it is not necessary to use a general-purpose data structure.
11.4 How do you connect the Iterator design pattern to a Composite design pattern?
In the example, if you want to examine a composite object architecture, you may need to iterate over the objects. Also, if you want to do some special activities with some branches, you may need to iterate over its leaf nodes and non-leaf nodes.
11.5 In your implementation, in the interface, you defined only one method, DisplayDetails. But you are using additional methods for the addition and removal of objects in the composite class (CompositeEmployee). Why are you not putting these methods in the interface?
Nice observation. Even the GoF discussed this. Let’s see what happens if you put the AddEmployee(...) and RemoveEmployee(...) methods in the interface. In that case, the leaf nodes need to implement these addition and removal operations. But will it be meaningful in this case? The answer is no. In this case, it may appear that you lose transparency, but I believe that you have more safety because I blocked the meaningless operations in the leaf nodes. This is why the GoF mentioned that this kind of decision involves a trade-off between safety and transparency.
11.6 I want to use an abstract class instead of an interface. Is this allowed?
In most cases, the simple answer is yes, but you need to understand the difference between an abstract class and an interface. In a typical scenario, you may find that one of them is more useful than the other. Throughout the book, I present only simple and easy-to-understand examples, so you may not see much difference between them.
In the “Q&A Session” section in Chapter 3, which covered the Builder pattern, I discussed how to decide between an abstract class and an interface.