Home/Design Patterns/Composite Pattern

Composite Pattern

🎯 Explain Like I'm 5...

Imagine you have a big toy box! Inside it, you can put toys OR smaller boxes that also contain toys (or even MORE boxes)! 📦

😟 The Problem:

  • Some things are SINGLE items (a toy car 🚗)
  • Some things are GROUPS of items (a box with many toys 📦)
  • You want to treat BOTH the same way - count them, play with them, organize them!

💡 The Solution - The Composite Pattern!

  • Make boxes and toys work the SAME way! 🎁
  • A single toy can say: 'I'm 1 toy!'
  • A box can say: 'I have 5 things inside me!' (by asking each thing inside)
  • You don't need to know if you're holding a toy or a box - both answer the same questions! 🎉

🌟 The Key Idea:

Treat single objects and groups of objects THE SAME WAY! Like folders on your computer - a folder can contain files OR more folders, and you can copy, move, or delete them all the same way! 💻

🚀 Why Is This Pattern Useful?

  • Build tree structures - like folders, company hierarchies, or menus!
  • Treat individual objects and groups uniformly!
  • Add new types of components easily without breaking existing code!

📋 Pattern Purpose

The Composite pattern lets you compose objects into tree structures to represent part-whole hierarchies. It allows clients to treat individual objects and compositions of objects uniformly.

⚡ When to Use Composite Pattern

  • You want to represent part-whole hierarchies of objects
  • You want clients to treat all objects in the structure uniformly
  • You're building tree structures (files/folders, menus, organization charts)
  • You want to ignore the difference between compositions and individual objects

🏗️ Structure Components

Component (Interface):

Declares the interface for objects in the composition

Leaf (Individual Item):

Represents individual objects with no children

Composite (Container):

Stores child components and implements child-related operations

💻 Java Implementations

Example 1: File System (Files and Folders)

A tree structure where folders can contain files or other folders.

FileSystemComponent.java
java
1
2
3
4
5
6
// Component - Common interface for both files and folders
public interface FileSystemComponent {
void display(String indent);
int getSize();
String getName();
}
File.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Leaf - Individual file (cannot contain other components)
public class File implements FileSystemComponent {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public void display(String indent) {
System.out.println(indent + "📄 " + name + " (" + size + " KB)");
}
@Override
public int getSize() {
return size;
}
@Override
public String getName() {
return name;
}
}
Folder.java
java
1
2
3
4
5
6
7
8
9
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
41
42
43
44
45
46
import java.util.ArrayList;
import java.util.List;
// Composite - Folder that can contain files or other folders
public class Folder implements FileSystemComponent {
private String name;
private List<FileSystemComponent> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
// Add a child component (file or folder)
public void add(FileSystemComponent component) {
children.add(component);
}
// Remove a child component
public void remove(FileSystemComponent component) {
children.remove(component);
}
@Override
public void display(String indent) {
System.out.println(indent + "📁 " + name + " (" + getSize() + " KB total)");
// Recursively display all children
for (FileSystemComponent child : children) {
child.display(indent + " ");
}
}
@Override
public int getSize() {
// Calculate total size by summing all children
int totalSize = 0;
for (FileSystemComponent child : children) {
totalSize += child.getSize();
}
return totalSize;
}
@Override
public String getName() {
return name;
}
}
FileSystemDemo.java
java
1
2
3
4
5
6
7
8
9
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// Client code - treats files and folders uniformly
public class FileSystemDemo {
public static void main(String[] args) {
// Create files
File file1 = new File("resume.pdf", 150);
File file2 = new File("photo.jpg", 2000);
File file3 = new File("document.docx", 500);
File file4 = new File("code.java", 50);
File file5 = new File("readme.txt", 10);
// Create folders
Folder documents = new Folder("Documents");
Folder photos = new Folder("Photos");
Folder projects = new Folder("Projects");
Folder root = new Folder("My Computer");
// Build the tree structure
documents.add(file1);
documents.add(file3);
photos.add(file2);
projects.add(file4);
projects.add(file5);
root.add(documents);
root.add(photos);
root.add(projects);
// Display the entire file system
// We treat the root folder just like any component!
root.display("");
System.out.println("\n--- Individual component ---");
// We can also display just a file
file1.display(" ");
System.out.println("\n--- Sub-folder ---");
// Or just a folder
documents.display("");
}
}
/* Output:
📁 My Computer (2710 KB total)
📁 Documents (650 KB total)
📄 resume.pdf (150 KB)
📄 document.docx (500 KB)
📁 Photos (2000 KB total)
📄 photo.jpg (2000 KB)
📁 Projects (60 KB total)
📄 code.java (50 KB)
📄 readme.txt (10 KB)
--- Individual component ---
📄 resume.pdf (150 KB)
--- Sub-folder ---
📁 Documents (650 KB total)
📄 resume.pdf (150 KB)
📄 document.docx (500 KB)
*/

Example 2: Company Hierarchy (Employees and Departments)

An organizational structure with individual employees and departments containing employees.

Employee.java
java
1
2
3
4
5
6
// Component interface
public interface Employee {
String getName();
double getSalary();
void showDetails(String indent);
}
Developer.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Leaf - Individual employee
public class Developer implements Employee {
private String name;
private double salary;
private String position;
public Developer(String name, double salary, String position) {
this.name = name;
this.salary = salary;
this.position = position;
}
@Override
public String getName() {
return name;
}
@Override
public double getSalary() {
return salary;
}
@Override
public void showDetails(String indent) {
System.out.println(indent + "👨‍💻 " + position + ": " + name +
" ($" + salary + ")");
}
}
Designer.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Leaf - Another type of individual employee
public class Designer implements Employee {
private String name;
private double salary;
public Designer(String name, double salary) {
this.name = name;
this.salary = salary;
}
@Override
public String getName() {
return name;
}
@Override
public double getSalary() {
return salary;
}
@Override
public void showDetails(String indent) {
System.out.println(indent + "🎨 Designer: " + name +
" ($" + salary + ")");
}
}
Department.java
java
1
2
3
4
5
6
7
8
9
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
41
42
43
44
import java.util.ArrayList;
import java.util.List;
// Composite - Department that contains employees or sub-departments
public class Department implements Employee {
private String name;
private List<Employee> employees = new ArrayList<>();
public Department(String name) {
this.name = name;
}
public void add(Employee employee) {
employees.add(employee);
}
public void remove(Employee employee) {
employees.remove(employee);
}
@Override
public String getName() {
return name;
}
@Override
public double getSalary() {
// Total budget is sum of all employee salaries
double total = 0;
for (Employee employee : employees) {
total += employee.getSalary();
}
return total;
}
@Override
public void showDetails(String indent) {
System.out.println(indent + "🏢 " + name +
" (Budget: $" + getSalary() + ")");
for (Employee employee : employees) {
employee.showDetails(indent + " ");
}
}
}
CompanyDemo.java
java
1
2
3
4
5
6
7
8
9
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
41
42
43
44
45
46
47
48
49
50
public class CompanyDemo {
public static void main(String[] args) {
// Create individual employees
Developer dev1 = new Developer("Alice", 80000, "Senior Developer");
Developer dev2 = new Developer("Bob", 70000, "Junior Developer");
Developer dev3 = new Developer("Charlie", 75000, "Developer");
Designer designer1 = new Designer("Diana", 65000);
Designer designer2 = new Designer("Eve", 60000);
// Create departments
Department techDept = new Department("Technology Department");
Department designDept = new Department("Design Department");
Department company = new Department("Tech Innovations Inc.");
// Build the hierarchy
techDept.add(dev1);
techDept.add(dev2);
techDept.add(dev3);
designDept.add(designer1);
designDept.add(designer2);
company.add(techDept);
company.add(designDept);
// Display the entire company structure
company.showDetails("");
System.out.println("\n--- Just the tech department ---");
techDept.showDetails("");
}
}
/* Output:
🏢 Tech Innovations Inc. (Budget: $350000.0)
🏢 Technology Department (Budget: $225000.0)
👨‍💻 Senior Developer: Alice ($80000.0)
👨‍💻 Junior Developer: Bob ($70000.0)
👨‍💻 Developer: Charlie ($75000.0)
🏢 Design Department (Budget: $125000.0)
🎨 Designer: Diana ($65000.0)
🎨 Designer: Eve ($60000.0)
--- Just the tech department ---
🏢 Technology Department (Budget: $225000.0)
👨‍💻 Senior Developer: Alice ($80000.0)
👨‍💻 Junior Developer: Bob ($70000.0)
👨‍💻 Developer: Charlie ($75000.0)
*/

Example 3: Graphics System (Shapes and Groups)

A drawing system where you can group shapes and treat groups like individual shapes.

Graphic.java
java
1
2
3
4
5
// Component interface
public interface Graphic {
void draw();
void move(int x, int y);
}
Circle.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Leaf - Individual shape
public class Circle implements Graphic {
private int x, y, radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing Circle at (" + x + "," + y +
") with radius " + radius);
}
@Override
public void move(int newX, int newY) {
this.x = newX;
this.y = newY;
System.out.println("Moving Circle to (" + x + "," + y + ")");
}
}
Rectangle.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Leaf - Another individual shape
public class Rectangle implements Graphic {
private int x, y, width, height;
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing Rectangle at (" + x + "," + y +
") with size " + width + "x" + height);
}
@Override
public void move(int newX, int newY) {
this.x = newX;
this.y = newY;
System.out.println("Moving Rectangle to (" + x + "," + y + ")");
}
}
GraphicGroup.java
java
1
2
3
4
5
6
7
8
9
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
import java.util.ArrayList;
import java.util.List;
// Composite - Group of graphics
public class GraphicGroup implements Graphic {
private String name;
private List<Graphic> graphics = new ArrayList<>();
public GraphicGroup(String name) {
this.name = name;
}
public void add(Graphic graphic) {
graphics.add(graphic);
}
public void remove(Graphic graphic) {
graphics.remove(graphic);
}
@Override
public void draw() {
System.out.println("Drawing Group: " + name);
for (Graphic graphic : graphics) {
graphic.draw();
}
}
@Override
public void move(int x, int y) {
System.out.println("Moving entire group: " + name);
// Move all graphics in the group
for (Graphic graphic : graphics) {
graphic.move(x, y);
}
}
}
GraphicsDemo.java
java
1
2
3
4
5
6
7
8
9
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class GraphicsDemo {
public static void main(String[] args) {
// Create individual shapes
Circle circle1 = new Circle(10, 10, 5);
Circle circle2 = new Circle(20, 20, 8);
Rectangle rect1 = new Rectangle(5, 5, 10, 15);
Rectangle rect2 = new Rectangle(30, 30, 20, 10);
// Create groups
GraphicGroup group1 = new GraphicGroup("Circles");
group1.add(circle1);
group1.add(circle2);
GraphicGroup group2 = new GraphicGroup("Rectangles");
group2.add(rect1);
group2.add(rect2);
GraphicGroup mainGroup = new GraphicGroup("All Shapes");
mainGroup.add(group1);
mainGroup.add(group2);
// Draw everything - uniform treatment!
System.out.println("=== Drawing all shapes ===");
mainGroup.draw();
System.out.println("\n=== Moving all shapes ===");
// Move the entire group at once
mainGroup.move(100, 100);
System.out.println("\n=== Drawing just circles ===");
group1.draw();
}
}
/* Output:
=== Drawing all shapes ===
Drawing Group: All Shapes
Drawing Group: Circles
Drawing Circle at (10,10) with radius 5
Drawing Circle at (20,20) with radius 8
Drawing Group: Rectangles
Drawing Rectangle at (5,5) with size 10x15
Drawing Rectangle at (30,30) with size 20x10
=== Moving all shapes ===
Moving entire group: All Shapes
Moving entire group: Circles
Moving Circle to (100,100)
Moving Circle to (100,100)
Moving entire group: Rectangles
Moving Rectangle to (100,100)
Moving Rectangle to (100,100)
=== Drawing just circles ===
Drawing Group: Circles
Drawing Circle at (100,100) with radius 5
Drawing Circle at (100,100) with radius 8
*/

🌍 Real-World Examples

  • 📁File Systems: Folders containing files and other folders
  • 🖼️GUI Components: Panels containing buttons, text fields, or other panels
  • 🏢Organization Charts: Departments containing employees and sub-departments
  • 📋Menu Systems: Menus containing menu items or submenus
  • 🎨Graphics Editors: Groups of shapes that can be moved/scaled together

✅ Benefits

  • Uniform Treatment: Treat individual and composite objects the same way
  • Easy to Add New Components: Can easily add new types of leaves or composites
  • Simplified Client Code: Clients don't need to distinguish between simple and complex elements
  • Natural Tree Structure: Perfect for hierarchical data

⚠️ Drawbacks

  • ⚠️Overly General: Can make design too general, harder to restrict components
  • ⚠️Type Safety: May be difficult to restrict what types can be added to composite
  • ⚠️Complexity: Adds complexity for simple hierarchies

🔑 Key Points to Remember

  • 1️⃣Composite lets you treat individual objects and compositions uniformly
  • 2️⃣Use it for tree structures (part-whole hierarchies)
  • 3️⃣Both Leaf and Composite implement the same Component interface
  • 4️⃣Composite delegates operations to its children
  • 5️⃣Makes it easy to add new kinds of components

💪 Practice Scenarios

  • Build a menu system with menus, submenus, and menu items
  • Create an expression evaluator (numbers, operators, and complex expressions)
  • Design a task management system with tasks and task groups
  • Implement a component hierarchy for a GUI framework
  • Build a file compression system that can compress individual files or entire directories