How to implement Composite pattern in Java?

Photo by Markus Spiske on Pexels.com

Let’s say you are the manager of a project.

You have been asked to calculate the total cost to your company of your entire team.

You have a service development team with two members.

You have a UI development team with two members.

You have a Quality Assurance team with two members.

You have a business analyst.

Now let’s use composite pattern to calculate the total cost to company.

Using this pattern we are going to treat an employee the same way we treat a team.

We are going to fire the same method getCostToCompany() on both the employee and the team.

If it is a team , the code is going to iterate through all the employees in its team and find the sum of their costs.

If it is an employee , it is going to just return the employee’s own cost to company.

That describes the Composite pattern in action.

Putting it in a simplistic description:

If we want to treat an individual object the same way as a group of related objects we use composite pattern.

Lets start from the client code:

package structural.composite.pattern;

public class Client {

	public static void main(String a[]) {

		IndividualEmployee serviceDeveloper1 = new IndividualEmployee("Kavin");

		serviceDeveloper1.setCostToCompany(50000);

		IndividualEmployee serviceDeveloper2 = new IndividualEmployee("Murali");

		serviceDeveloper2.setCostToCompany(60000);

		IndividualEmployee tester1 = new IndividualEmployee("Kiran");

		tester1.setCostToCompany(40000);
		IndividualEmployee tester2 = new IndividualEmployee("Ayisha");

		tester2.setCostToCompany(35000);

		IndividualEmployee uiDeveloper1 = new IndividualEmployee("Vinitha");

		uiDeveloper1.setCostToCompany(70000);
		IndividualEmployee uiDeveloper2 = new IndividualEmployee("Agalya");

		uiDeveloper2.setCostToCompany(85000);

		EmployeeTeam serviceDevelopmentTeam = new EmployeeTeam("Service Development Team");

		serviceDevelopmentTeam.add(serviceDeveloper1);
		serviceDevelopmentTeam.add(serviceDeveloper2);

		EmployeeTeam uiDevelopmentTeam = new EmployeeTeam("UI Development Team");

		uiDevelopmentTeam.add(uiDeveloper1);
		uiDevelopmentTeam.add(uiDeveloper2);

		EmployeeTeam qaTeam = new EmployeeTeam("QA team");

		qaTeam.add(tester1);
		qaTeam.add(tester2);
		
		
		
		EmployeeTeam developmentTeam = new EmployeeTeam("Development Team");
		developmentTeam.add(serviceDevelopmentTeam);
		developmentTeam.add(uiDevelopmentTeam);
		
		
		IndividualEmployee businessAnalyst = new IndividualEmployee("Rama");
		businessAnalyst.setCostToCompany(60000);
		
		EmployeeTeam entireTeam = new EmployeeTeam("Entire team");
		entireTeam.add(developmentTeam);
		entireTeam.add(qaTeam);
		entireTeam.add(businessAnalyst);
		
		
		System.out.println("Total Cost to Company: "+entireTeam.getCostToCompany());

	}

}

As you can see ‘entireTeam’ represents the whole team.

It has two sub teams ‘Development team’ and ‘QA team ‘ and an individual business analyst added to it as well.

The development team in turn has two sub teams : Service Development team and UI Development team.

The Service and UI Development teams in turn have two developers in them each.

Also the QA team has two testers in them.

Both the ‘EmployeeTeam’ and ‘IndividualEmployee’ extend the same base class ‘Employee’ and overrides the method getCostToCompany() in their own way to utilize Composite Pattern,

Only a single method is fired on the entireTeam – getCostToCompany() to get the cost of the company.

Let’s look at the base class of both EmployeeTeam and IndividualEmployee:

package structural.composite.pattern;

import java.util.ArrayList;
import java.util.List;

public abstract class Employee {

	protected long costToCompany;

	protected String name;

	protected List<Employee> employees = new ArrayList<Employee>();


	public String getName() {
		
		return this.name;
	}
	
	void add(Employee employee) throws Exception {

		throw new UnsupportedOperationException("Only team can add an employee");
	}

	void remove(Employee employee) throws Exception {

		throw new UnsupportedOperationException("Only team can remove an employee");
	}

	abstract long getCostToCompany();

}

The Employee base class has:

  • A costToCompany field which carries the cost to company value.
  • A name attribute to represent the employee name or the team name
  • An add() operation used to add employees if it is an employee team. An employee cannot add an employee to itself though it extends this base class. Hence by default we throw an UnsupportedOperationException. The EmployeeTeam overrides this method to add the employee to its team.
  • Similarly we have a remove() operation to remove members from the team which again can be supported only by the ‘EmployeeTeam’ classes
  • We have an abstract getCostToCompany() which is the major method utilizing the Composite design pattern. In case of an employee it will return the costToCompany set in the Individual Employee class. In case of a team , it will iterate through all the employees added to the team , sum up their costs and return the value.

Here is the IndividualEmployee class:

package structural.composite.pattern;

public class IndividualEmployee extends Employee {

	public IndividualEmployee(String name) {

		this.name = name;
	}

	@Override
	long getCostToCompany() {

		return this.costToCompany;
	}

	public void setCostToCompany(long cost) {

		this.costToCompany = cost;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		IndividualEmployee other = (IndividualEmployee) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

}

Here is the EmployeeTeam class extending the same base classs:

package structural.composite.pattern;

public class EmployeeTeam extends Employee {

	public EmployeeTeam(String name) {

		this.name = name;
	}

	@Override
	void add(Employee employee) {

		this.employees.add(employee);
	}

	@Override
	void remove(Employee employee) {

		this.employees.remove(employee);
	}

	@Override
	long getCostToCompany() {

		long cost = 0;

		if (this.employees != null) {
			for (Employee e : this.employees) {

				if (e instanceof IndividualEmployee ind) {
					System.out.println("Cost to Company of " + ind.getName());
					System.out.println(ind.getCostToCompany());

				}

				cost += e.getCostToCompany();
			}
		}
		return cost;
	}

}

Here we are overriding add() and remove() methods to add and remove employees from the list of employees respectively. Also we implement the getCostToCompany() and iterate over all the Individual Employees in the team , to sum up their costs.

Here is the output :

Cost to Company of Kavin
50000
Cost to Company of Murali
60000
Cost to Company of Vinitha
70000
Cost to Company of Agalya
85000
Cost to Company of Kiran
40000
Cost to Company of Ayisha
35000
Cost to Company of Rama
60000
Total Cost to Company: 400000

An elegant way to treat a ‘team’ and ’employee’ the same way to get the total cost to company.

Quoting Gang of Four,

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and composites of objects uniformly

And they suggest to use it when :

– you want to represent part-whole hierarchies to objects (in our case Employee – Employee Team represent part-whole hierarchy)

– you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly. ( in our cases we treat both the team and the individual member the same way to get the cost to company)

Here is the code :

https://github.com/vijaysrj/designPatternsGoF/tree/master/src/structural/composite/pattern


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading