Java Composite Design Pattern Example
1. Introduction
In this post, we feature a comprehensive Example on Java Composite Design Pattern. In a large scale Java application built for enterprises, there are certain pre-defined coding standards and structures to be followed. These standards and structures assist in development of a code that is organised and easily manageable. This also ensures that the code is structured in a predefined manner and thus it is easily understandable by any new developer joining the project.
In order to ensure that these coding structures are standardised, Java defines a large bunch of design patterns which revolve around the various aspects of coding like the definition of class, the structure of child-parent classes, the creation of objects and the management of object behaviour. There are three primary patterns which are briefly explained below.
- Creational Pattern: This pattern category provides five different patterns that focus on the logic of instantiation of objects while concealing the actual implementation of the objects. Thus, this pattern controls the objects that should be created for every module.
- Structural Pattern: This pattern category provides us with seven different patterns to help the developers organise the class structure so that the desired classes and features are exposed in a desired manner. Additionally, they offers solutions to problems like interfacing different types of objects, entities or applications.
- Behavioural Pattern: This pattern category is mainly associated with the way objects communicate with each other. This includes messaging queues, passing messages via connecting classes and others. There are nearly eleven such patterns to define the communication methodologies.
In this article, we would be discussing one of the structural patterns – Composite design pattern. The article would explain the concept behind this pattern with a real life example. The next section covers the pattern logically to explain each component of Composite design pattern. Further on, those sections will be implemented in code.
2. Understanding the Composite design pattern
A composite design pattern is used specifically when there is a need for abstraction for a group of objects being aggregated eventually in an implementor class. In a composite design pattern, there are three different types of classes. These are:
- Base Component classes
- Leaf Component classes
- Composite classes
- Client class
The diagram below explains this better using a Venn Diagram layout of how classes would be structured. Each of the components are explained in details further.
2.1 Base Component Class
A base component class in a composite design pattern is the class that actually implements the core object or the parent object for succeeding class. For instance, a Airways class, A Vehicle class, A Shape are also generic classes which will be then implemented by related sub classes. The base component class provides an abstract layer of functionalities required by the implementing subclasses. These subclasses will then have similar functionalities as their parent classes plus the features specific to themselves.
The primary reason for creating this base component class is not only to provide common functionalities to the implementing classes but also to allow the final composite class to utilise the Leaf classes in a single collection. This will be further explained in the coding part
2.2 Leaf classes
A leaf class is the class implementing the base component classes. The leaf classes are normally sub classes that will be later used in the composite classes in the form of collection or individual objects. For instance a TwoWheeler, a Rectangle, a Cone could be a leaf class where the Vehicle or the Shape class will be base component classes respectively. These leaf classes tend to implement a common behaviour i.e a common interface called the base component class. This interface is then used to generalise the behaviour of leaf classes in the composite classes.
2.3 Composite classes
A composite class is the class utilising the leaf classes using its base class for the definition. Such a class structuring where the objects are defined using their base component class names, is known as composite class. In such a class, the type of object is uncertain until initialised. For instance, consider a drawing board. A drawing board will contain different shapes but none of them are fixed until finally drawn or rather created.
Thus, in the above example, the DrawingBoard class would act as a composite class consisting of a collection of base component class Shape. These objects in the collections will be initialised to different leaf class instances as needed.
Thus, composite design pattern is all about organising different objects under a single umbrella to use them compositely in a single class.
A conventional analogy of composite design pattern in real world could be an organisation. An organisation can be considered as a composite class containing a single CEO, bunch of senior managers doing a similar job and a whole lot of employees doing different jobs and having a different skillset. A depiction of this is shown below.
This should clear the concept of composite class to a great extent. Now, let us move on to coding a design pattern.
2.4 Client class
A client class could be a class utilising composite class objects directly. The client class may or may not exist in a project depending on how we need to structure the code. In the article, we are focusing on implementing the pattern without depending on the client class. Hence, we would be discussing more on the client class currently.
3. Implementing composite design pattern
In order to implement and understand composite design pattern, we will need a few real life entities with common behaviour. Let us take the case of a real estate company. A real estate company sells a variety of properties. A property is the base component class here.
Property.java
1 2 3 4 5 6 | package com.javacodegeeks.basecomponent; public interface Property { public void purchase(); public void sell(); } |
The above code shows the component class or rather the component interface that has two basic functionalities – Buy and sell. Every property item irrespective of its type can be bought as well as sold. Thus, these are the two common functions which will be defined further.
The next step is to create different types of properties that might be possible. Here we are considering three different types of properties – Apartments, Bungalow and Tenaments. The code for each has been shown below.
Apartment.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package com.javacodegeeks.leaf; import com.javacodegeeks.basecomponent.Property; public class Apartment implements Property{ float price; String address; String builder; public float getPrice() { return price; } public void setPrice( float price) { this .price = price; } public String getAddress() { return address; } public void setAddress(String address) { this .address = address; } public String getBuilder() { return builder; } public void setBuilder(String builder) { this .builder = builder; } @Override public void purchase() { // TODO Auto-generated method stub } @Override public void sell() { // TODO Auto-generated method stub } } |
Tenaments.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package com.javacodegeeks.leaf; import com.javacodegeeks.basecomponent.Property; public class Tenaments implements Property { float price; String address; String builder; public float getPrice() { return price; } public void setPrice( float price) { this .price = price; } public String getAddress() { return address; } public void setAddress(String address) { this .address = address; } public String getBuilder() { return builder; } public void setBuilder(String builder) { this .builder = builder; } @Override public void purchase() { // TODO Auto-generated method stub } @Override public void sell() { // TODO Auto-generated method stub } } |
Bungalow.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package com.javacodegeeks.leaf; import com.javacodegeeks.basecomponent.Property; public class Bungalow implements Property{ float price; String address; String builder; public float getPrice() { return price; } public void setPrice( float price) { this .price = price; } public String getAddress() { return address; } public void setAddress(String address) { this .address = address; } public String getBuilder() { return builder; } public void setBuilder(String builder) { this .builder = builder; } @Override public void purchase() { // TODO Auto-generated method stub } @Override public void sell() { // TODO Auto-generated method stub } } |
As it can be seen, there are three different classes now that can implement the Property
class created as a component interface. The above three classes are called as leaf classes as they implement a component interface. Finally, the next step is to consume these classes and create the final block for the composite design pattern – A composite class.
A composite class could be any module that uses a collection of different types of Property
. Let us finally develop an application class.
Application.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import java.util.ArrayList; import java.util.Scanner; import com.javacodegeeks.basecomponent.Property; import com.javacodegeeks.leaf.Apartment; import com.javacodegeeks.leaf.Bungalow; import com.javacodegeeks.leaf.Tenaments; public class Application { public static void main(String[] args) { ArrayList properties = new ArrayList(); Scanner in = new Scanner(System.in); for ( int i= 0 ;i< 5 ;i++) { Property p = null ; System.out.println( "Choose type of property to add" ); System.out.println( "1. Apartment\n2. Tenaments\n3. Bungalow" ); int type = in.nextInt(); //Initialise with respective type if (type == 1 ) { p = new Apartment(); } else if (type== 2 ){ p = new Tenaments(); } else { p = new Bungalow(); } //Gather the properties //Do the desired task with the properties } } } |
In the above class, notice carefully that the ArrayList
being used is of type Property
. Although it is a class, Java consider interface to be a type when you to define a generic instance. These element which are qualified by interface type can then be initialised using any objects of the class implementing the respective interface. Thus, the list in the above code can contain three different types of object – Apartments, Tenaments & Bungalow.
In this manner, the class contains all the three types of objects, without really declaring an object of corresponding type. Such a class is called a composite class. This and several other such classes, collectively form a project using composite design pattern. It should be understood here that composite design pattern is used only when there are a large variety of entities with similar properties and these entities need to be used together in various classes of the application class.
4. Benefits of using Composite Design pattern
The benefits of composite design pattern are better understood when the pattern is not used. Imagine two different scenarios.
- A scenario where different types of properties are all handled by a single class named Property
- A scenario where there is no generic interface and all the classes are independent
In the first scenario, all the data will be gathered by the class Property. Thus, the amount of data and memory occupied by the object will be higher since the data from multiple property types will be present in every object. This increases redundancy in terms of data
In the second scenario, all the data will be distributed property wise in respective classes. However, even the common data will be distributed among the classes which will be a code redundancy. This code redundancy becomes difficult to manage when the property types increase or rather the leaf classes increase.
In this manner, Composite design pattern not only helps in optimising memory usage but also helps in reducing code.
5. Points to note
For the composite design pattern, below are few points that need to be noted
- Apply composite design pattern only on the objects that have common group behaviour.
- Composite design pattern is used to create a tree leaf structure using the classes to represent a part-whole structure.
- When dealing with Tree-structured data, programmers often have to discriminate between a leaf-node and a branch. This makes code more complex, and therefore, error prone. The solution is an interface that allows treating complex and primitive objects uniformly.
- In object-oriented programming, a composite is an object designed as a composition of one-or-more similar objects, all exhibiting similar functionality. This is known as a “has-a” relationship between objects.
6. Scenarios where composite pattern is a NO
With Composite design pattern, it is a tough job to restrict the type of components in the composite class. So, composite design pattern shouldn’t be used when we wish to conceal the partial or full hierarchy of the objects.
Composite design pattern generalises the design to a great extent. Not every developer is fond of developing a code that is so generalised. When the aim is to develop a certain level of complexity in the code structure, composite design pattern is a bad choice to use.
7. Java Composite Design Pattern Example – Conclusion
Every design pattern is defined with a different end goal. However, each pattern primarily focuses on providing the same output – A well-organised code structure with memory optimised execution. The Composite design pattern is one such design pattern that is defined to allow the user to generalise the behaviour of similar objects and utilise them in a single composite class. The composite design pattern makes it easier to group the objects by just creating a wrapper interface above them. Thus, Composite design pattern is best used in a tree-leaf kind of code structure creation.