Menu
Coddy logo textTech

Composite Pattern

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 96 of 107.

The Composite pattern lets you treat individual objects and groups of objects uniformly. While State changes behavior based on internal state, Composite builds tree structures where both leaves and containers share the same interface.

This pattern is perfect for hierarchical structures like file systems, organization charts, or UI components. In Go, we define a common interface that both individual items and containers implement:

type Component interface {
    GetSize() int
}

type File struct {
    Name string
    Size int
}

func (f File) GetSize() int {
    return f.Size
}

The composite (container) holds children and implements the same interface by aggregating their results:

type Folder struct {
    Name     string
    Children []Component
}

func (f *Folder) Add(c Component) {
    f.Children = append(f.Children, c)
}

func (f Folder) GetSize() int {
    total := 0
    for _, child := range f.Children {
        total += child.GetSize()
    }
    return total
}

Now you can nest folders within folders, and calling GetSize() works identically whether it's a single file or an entire directory tree:

docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})

root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})

fmt.Println(root.GetSize())  // 175

Composite is ideal when you need to represent part-whole hierarchies and want clients to treat individual objects and compositions identically.

challenge icon

Challenge

Easy

Let's build an organization chart system using the Composite pattern! You'll create a structure where both individual employees and departments (which contain employees or other departments) can be treated uniformly—perfect for calculating total salaries across any part of the organization.

You'll organize your code across three files:

  • component.go: Define the common interface that both individuals and groups will implement.

    Create an OrgComponent interface with two methods:

    • GetSalary() int — returns the total salary for this component
    • GetName() string — returns the name of the employee or department
  • organization.go: Implement both the leaf (individual employee) and composite (department) types.

    Create an Employee struct with Name (string) and Salary (int) fields. Its GetSalary() returns its own salary, and GetName() returns its name.

    Create a Department struct with a Name (string) field and a Members slice that holds OrgComponent items. Add an Add(c OrgComponent) method to add members. Its GetSalary() should return the sum of all members' salaries, and GetName() returns the department name.

  • main.go: Build an organization structure and calculate salaries.

    Read the number of employees. For each employee, read their name and salary, creating Employee instances. Then read the number of departments. For each department, read its name and the count of member indices, followed by those indices (0-based, referring to previously created employees or departments in order of creation).

    After building the structure, read a final index and print that component's name and total salary in the format: [name]: [salary]

The following inputs will be provided:

  • Number of employees, then each employee's name and salary on separate lines
  • Number of departments, then each department's name, member count, and member indices
  • Final index to query

For example, given:

3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3

Your output should be:

Engineering: 165000

And given:

4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4

Your output should be:

QA: 125000

And given:

2
Alice
50000
Bob
60000
0
1

Your output should be:

Bob: 60000

Notice how GetSalary() works identically whether you call it on a single employee or an entire department—the Composite pattern lets you treat individuals and groups uniformly through the same interface!

Cheat sheet

The Composite pattern treats individual objects and groups of objects uniformly through a shared interface. It's ideal for hierarchical structures like file systems, organization charts, or UI components.

Define a common interface that both individual items and containers implement:

type Component interface {
    GetSize() int
}

Implement the leaf (individual item):

type File struct {
    Name string
    Size int
}

func (f File) GetSize() int {
    return f.Size
}

Implement the composite (container) that holds children and aggregates their results:

type Folder struct {
    Name     string
    Children []Component
}

func (f *Folder) Add(c Component) {
    f.Children = append(f.Children, c)
}

func (f Folder) GetSize() int {
    total := 0
    for _, child := range f.Children {
        total += child.GetSize()
    }
    return total
}

Usage example showing uniform treatment of individual items and compositions:

docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})

root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})

fmt.Println(root.GetSize())  // 175

Try it yourself

package main

import "fmt"

func main() {
	// Read number of employees
	var numEmployees int
	fmt.Scanln(&numEmployees)

	// Store all components (employees and departments) in order of creation
	var components []OrgComponent

	// TODO: Read each employee's name and salary
	// Create Employee instances and add them to components slice
	for i := 0; i < numEmployees; i++ {
		var name string
		var salary int
		fmt.Scanln(&name)
		fmt.Scanln(&salary)
		// TODO: Create employee and add to components
	}

	// Read number of departments
	var numDepartments int
	fmt.Scanln(&numDepartments)

	// TODO: Read each department's name, member count, and member indices
	// Create Department instances, add members by index, and add to components
	for i := 0; i < numDepartments; i++ {
		var deptName string
		var memberCount int
		fmt.Scanln(&deptName)
		fmt.Scanln(&memberCount)
		// TODO: Create department, read member indices, add members, add to components
	}

	// Read the final index to query
	var queryIndex int
	fmt.Scanln(&queryIndex)

	// TODO: Print the component's name and total salary in format: [name]: [salary]
	// Example: fmt.Printf("%s: %d\n", name, salary)
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming