The composite pattern is a structural design pattern which allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Lets take an example of creating a organizational tree hierarchies . We want to create a tree like structure where given the root employee like ceo we should be able to traverse through the hierarchy.
We will be creating the below class structures.
This pattern gives us a way to create a tree structure that can handle a nested group of employees who can be Managers and Individual contributors in the same structure. By putting Managers and Individual contributors in the same structure we create a part-whole hierarchy that is a tree of objects that is made of parts Managers and Individual contributors but that can be treated as a whole, like one big organizational hierarchy.
In this pattern we have a tree structure of Managers and Individual contributors and any employee is a composition because it can contain both other managers and IC. The individual objects are just the IC and they do not hold other objects. Using a design that follows the Composite Pattern is going to allow us to write some simple code that can apply the same operation (like traversing) over the entire employee structure.
lets code the example
Employee abstract class
abstract class Employee(name: String, id: Int, salary: Int, desination: String) { def print() { throw new UnsupportedOperationException() } def addReportee(emp: Employee) { throw new UnsupportedOperationException() } def removeReportee(employee: Employee) { throw new UnsupportedOperationException() } }
Lets code the individual contributor class which extends the employee abstract class
class IndividualContributor(name: String, id: Int, salary: Int, designation: String) extends Employee(name, id, salary, designation) { override def print() = { println("#################################################################") println("name of employee " + name) println("id of employee " + id) println("salary of employee " + salary) println("designation of employee " + designation) } }
Lets code the manager class which also extends from the same employee base class. The manager class also has support for additional methods like adding addReportee and removeReportee, but these methods does not make sense on individual contributor class so if we call these methods in individual contributor class we will throw a UnsupportedOperationException.
class Manager(name: String, id: Int, salary: Int, designation: String) extends Employee(name, id, salary, designation) { val listOfReportee = new scala.collection.mutable.ListBuffer[Employee] override def print() { println("#################################################################") println("name of employee " + name) println("id of employee " + id) println("salary of employee " + salary) println("designation of employee " + designation) listOfReportee.foreach { emp => emp.print(); } } override def addReportee(emp: Employee) { listOfReportee += emp } override def removeReportee(employee: Employee) { listOfReportee -= employee } }
Finally we will write the client class were we will create managers and the individual contributors objects and creating the required structure.And finally we will be calling the print method on the top most object which will be called recursively if the object is of type manager or else the print method of the individual contributor will be called.
object Test extends App{ val suresh_ic = new IndividualContributor("suresh_ic", 1, 1000, "software enginner"); val samanth_ic = new IndividualContributor("samanth_ic", 2, 2000, " senior software enginner"); val rakesh_ic = new IndividualContributor("rakesh_ic", 3, 3000, "seniour software enginner"); val raghu_ic = new IndividualContributor("raghu_ic", 2, 2000, "lead enginner"); val veeresh_ic = new IndividualContributor("veeresh_ic", 3, 3000, "associate software enginner"); val stephan_ic = new IndividualContributor("stephan_ic", 4, 4000, "associate software enginner"); val roger_manager = new Manager("roger_manager", 4, 4000, "lead enginner"); roger_manager.addReportee(suresh_ic); roger_manager.addReportee(samanth_ic); roger_manager.addReportee(rakesh_ic); val rishab_manager = new Manager("rishab_manager", 1, 1000, "manager enginner"); rishab_manager.addReportee(roger_manager); rishab_manager.addReportee(raghu_ic); rishab_manager.addReportee(veeresh_ic); rishab_manager.addReportee(stephan_ic); rishab_manager.print(); }
Below will be the output
################################################################# name of employee rishab_manager id of employee 1 salary of employee 1000 designation of employee manager enginner ################################################################# name of employee roger_manager id of employee 4 salary of employee 4000 designation of employee lead enginner ################################################################# name of employee suresh_ic id of employee 1 salary of employee 1000 designation of employee software enginner ################################################################# name of employee samanth_ic id of employee 2 salary of employee 2000 designation of employee senior software enginner ################################################################# name of employee rakesh_ic id of employee 3 salary of employee 3000 designation of employee seniour software enginner ################################################################# name of employee raghu_ic id of employee 2 salary of employee 2000 designation of employee lead enginner ################################################################# name of employee veeresh_ic id of employee 3 salary of employee 3000 designation of employee associate software enginner ################################################################# name of employee stephan_ic id of employee 4 salary of employee 4000 designation of employee associate software enginner