Composite Pattern
Part of the Object Oriented Programming section of Coddy's Java journey — lesson 75 of 87.
The Composite Pattern is a structural design pattern that lets you compose objects into tree structures to represent part-whole hierarchies. It allows clients to treat individual objects and compositions of objects uniformly through a common interface.
Think of a file system: a folder can contain files and other folders, which can contain more files and folders. Whether you're dealing with a single file or an entire folder structure, you want to perform operations like "get size" the same way. The pattern defines a Component interface, Leaf objects (individual elements), and Composite objects (containers that hold children):
interface FileComponent {
void display(String indent);
int getSize();
}
class File implements FileComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public void display(String indent) {
System.out.println(indent + name + " (" + size + "KB)");
}
public int getSize() {
return size;
}
}
class Folder implements FileComponent {
private String name;
private List<FileComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileComponent component) {
children.add(component);
}
public void display(String indent) {
System.out.println(indent + name + "/");
for (FileComponent child : children) {
child.display(indent + " ");
}
}
public int getSize() {
return children.stream().mapToInt(FileComponent::getSize).sum();
}
}The composite delegates operations to its children, allowing recursive structures. Clients interact with the tree without knowing whether they're working with a leaf or a composite:
Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));
Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);
root.display("");
System.out.println("Total: " + root.getSize() + "KB");The Composite Pattern is ideal for representing hierarchies like organizational charts, UI components, or menu systems where you need to treat groups and individuals identically.
Challenge
EasyLet's build an organizational chart system using the Composite Pattern! You'll create a structure where departments can contain employees and other sub-departments, allowing you to calculate total salaries and display the hierarchy uniformly—whether you're looking at a single employee or an entire division.
You'll organize your code across four files:
OrganizationComponent.java: Define the component interface that both employees and departments will implement. It should declare two methods:showDetails(String indent)for displaying the component with proper indentation, andgetSalary()that returns the total salary as an integer.Employee.java: Create the leaf class representing individual workers. AnEmployeehas a name (String) and salary (int), both set through the constructor. WhenshowDetailsis called, it should print[indent][name]: $[salary]. ThegetSalarymethod simply returns the employee's salary.Department.java: Create the composite class that can hold both employees and sub-departments. ADepartmenthas a name (String) and maintains a list ofOrganizationComponentchildren. It should have anadd(OrganizationComponent component)method to add members. WhenshowDetailsis called, it prints[indent][name] Department, then callsshowDetailson each child with increased indentation (add two spaces). ThegetSalarymethod returns the sum of all children's salaries.Main.java: Build your organization! You'll receive four inputs: two employee names with their salaries, and a sub-department name with one employee.Create an "Engineering" department as your root. Add two employees to it using the first two name/salary pairs. Then create a sub-department using the third input, add one employee to it using the fourth name/salary pair, and add this sub-department to Engineering.
Call
showDetails("")on the Engineering department, then print the total salary in the formatTotal Salary: $[amount].
You will receive inputs in this order: employee1 name (String), employee1 salary (int), employee2 name (String), employee2 salary (int), sub-department name (String), employee3 name (String), employee3 salary (int).
For example, with inputs Alice, 75000, Bob, 65000, QA, Charlie, 55000, your output would be:
Engineering Department
Alice: $75000
Bob: $65000
QA Department
Charlie: $55000
Total Salary: $195000Notice how the same showDetails and getSalary methods work seamlessly whether called on a single employee or an entire department containing nested sub-departments. The composite structure handles the recursion automatically, letting you treat the whole hierarchy uniformly!
Cheat sheet
The Composite Pattern is a structural design pattern that composes objects into tree structures to represent part-whole hierarchies. It allows treating individual objects and compositions uniformly through a common interface.
The pattern consists of three key elements:
- Component: Interface defining common operations
- Leaf: Individual objects that implement the component interface
- Composite: Container objects that hold children and delegate operations to them
Example implementation of a file system:
// Component interface
interface FileComponent {
void display(String indent);
int getSize();
}
// Leaf class
class File implements FileComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public void display(String indent) {
System.out.println(indent + name + " (" + size + "KB)");
}
public int getSize() {
return size;
}
}
// Composite class
class Folder implements FileComponent {
private String name;
private List<FileComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileComponent component) {
children.add(component);
}
public void display(String indent) {
System.out.println(indent + name + "/");
for (FileComponent child : children) {
child.display(indent + " ");
}
}
public int getSize() {
return children.stream().mapToInt(FileComponent::getSize).sum();
}
}Using the composite structure:
Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));
Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);
root.display("");
System.out.println("Total: " + root.getSize() + "KB");The composite delegates operations to its children recursively. Clients interact with the tree without knowing whether they're working with a leaf or composite.
Use cases: Organizational charts, UI components, menu systems, file systems—any hierarchy where groups and individuals need identical treatment.
Try it yourself
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Read inputs
String emp1Name = scanner.nextLine();
int emp1Salary = Integer.parseInt(scanner.nextLine());
String emp2Name = scanner.nextLine();
int emp2Salary = Integer.parseInt(scanner.nextLine());
String subDeptName = scanner.nextLine();
String emp3Name = scanner.nextLine();
int emp3Salary = Integer.parseInt(scanner.nextLine());
// TODO: Create the "Engineering" department as the root
// TODO: Create and add two employees to Engineering using emp1 and emp2 data
// TODO: Create a sub-department using subDeptName
// TODO: Create and add one employee to the sub-department using emp3 data
// TODO: Add the sub-department to Engineering
// TODO: Call showDetails("") on the Engineering department
// TODO: Print the total salary in format: Total Salary: $[amount]
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsFields (Attributes)Constructor MethodConstructor OverloadingRecap - Simple Calculator4Inheritance
Basic Inheritance (extends)The super KeywordMethod Overriding (@Override)Constructor ChainingThe Object ClassSingle & Multilevel InheritWhy No Multi Class InheritRecap - Employee Hierarchy7Special Methods & Object Class
toString() Methodequals() and hashCode()clone() MethodcompareTo() and ComparableComparator InterfaceRecap - Custom Sorting2Access Modifiers & Encapsulate
Access Levels OverviewGetter and Setter MethodsInformation HidingThe final KeywordRecap - Bank Account Manager5Polymorphism
Method Overloading BasicsMethod Overriding (Run-Time)Upcasting and DowncastingThe instanceof OperatorAbstract Classes and MethodsRecap - Shape Calculator8Advanced OOP Concepts
Composition vs InheritanceAggregation vs CompositionInner Nested & Anonymous ClassEnums and Enum MethodsRecords (Java 16+)Sealed Classes (Java 17+)3Class Props & Static Member
Instance vs Static VariablesStatic MethodsStatic BlocksConstants (static final)Recap - Counter & Utility6Interfaces & Abstract Classes
Introduction to InterfacesImplementing InterfacesMulti Interface ImplemenDefault & Static in InterfaceAbstract Classes vs InterfacesFunctional InterfacesRecap - Payment System9Generics
Introduction to GenericsGeneric ClassesGeneric MethodsBounded Type ParametersWildcards (?, extends, super)Recap - Generic Container12Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite PatternIterator Pattern