Introduction

Composite pattern is used where we need to treat a group of objects in similar way as a single object. Composite pattern composes objects in term of a tree structure to represent part as well as whole hierarchy. This pattern creates a class that contains group of its own objects. This class provides ways to modify its group of same objects. Consider for example a program that manipulates a file system. A file system is a tree structure that contains branches which are folders as well as leaf nodes which are files. Folder object usually contains one or more file or folder objects and thus is a complex object where a file is a simple object. Since files and folders have many operations and attributes in common, such as moving and copying a file or a folder, listing file or folder attributes such as file name and size, it would be easier and more convenient to treat both file and folder objects uniformly by defining a File System Resource Interface.

Intent

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

Implementation

It has four participants:

  • Component is the abstraction for leafs and composites. It defines the interface that must be implemented by the objects in the composition.
  • Leafs are objects that have no children. They implement services described by the Component interface.
  • Composite stores child components in addition to implementing methods defined by the Component interface. Composites implement methods defined in the Component interface by delegating to child components.
  • Client manipulates the objects in the composition through the component interface.

Example

// Composite Design Pattern example of a company with different employee details
// A common interface for all employee 
interface Employee 
{ 
  public void showEmployeeDetails(); 
} 

class Developer implements Employee 
{ 
  private String name; 
  private long empId; 
  
  public Developer(long empId, String name) 
  { 
    this.empId = empId; 
    this.name = name; 
  } 
  
  @Override
  public void showEmployeeDetails() 
  { 
    System.out.println(empId+" " +name); 
  } 
} 

class Manager implements Employee 
{ 
  private String name; 
  private long empId; 

  public Manager(long empId, String name) 
  { 
    this.empId = empId; 
    this.name = name; 
  } 
  
  @Override
  public void showEmployeeDetails() 
  { 
    System.out.println(empId+" " +name); 
  } 
} 


// Class used to get Employee List and do the opertions like add or remove Employee
class CompanyDirectory implements Employee 
{ 
  private List<Employee> employeeList = new ArrayList<Employee>(); 
    
  @Override
  public void showEmployeeDetails() 
  { 
    for(Employee emp:employeeList) 
    { 
      emp.showEmployeeDetails(); 
    } 
  } 
    
  public void addEmployee(Employee emp) 
  { 
    employeeList.add(emp); 
  } 
    
  public void removeEmployee(Employee emp) 
  { 
    employeeList.remove(emp); 
  } 
} 

// Driver class 
public class Company 
{ 
  public static void main (String[] args) 
  { 
    Developer d1 = new Developer(100, "Lokesh Sharma"); 
    Developer d2 = new Developer(101, "Vinay Sharma"); 
    CompanyDirectory engDirectory = new CompanyDirectory(); 
    engDirectory.addEmployee(d1); 
    engDirectory.addEmployee(d2); 
    
    Manager m1 = new Manager(200, "Kushagra Garg"); 
    Manager m2 = new Manager(201, "Vikram Sharma "); 
    CompanyDirectory accDirectory = new CompanyDirectory(); 
    accDirectory.addEmployee(m1); 
    accDirectory.addEmployee(m2); 
  
    CompanyDirectory directory = new CompanyDirectory(); 
    directory.addEmployee(engDirectory); 
    directory.addEmployee(accDirectory); 
    directory.showEmployeeDetails(); 
  } 
}