Understanding Go's Composite Pattern: Building Hierarchical Structures
This article explains the Composite (part‑whole) design pattern in Go, illustrates its concept with natural and file‑system analogies, and provides complete Go code demonstrating how files and folders can be treated uniformly as tree nodes.
Composite Pattern
Objects that have a part‑whole relationship can be organized into a tree hierarchy, allowing clients uniform access to both individual (leaf) and composite (container) objects.
Understanding the Idea
The pattern groups resources that share a relationship into a consistent part‑and‑whole structure. A natural illustration is a leaf: each leaf contains smaller leaflets, which in turn contain finer veins, forming a self‑similar fractal‑like hierarchy.
Another illustration uses a collection of colored pens and shapes; pairing them produces richer colored patterns, showing how combining resources yields more functionality.
Observing a leaf reveals repeated sub‑structures at different scales, exemplifying the self‑similarity that the Composite pattern captures.
Code Demonstration
The example models a file‑system hierarchy, a classic Composite use case.
type Node interface {
// Add a child node
Add(child Node)
// Print the node name with indentation defined by space
Print(space int)
}File represents a leaf node; its Add does nothing, while Print outputs the file name with the appropriate indentation.
type File struct {
name string
}
// Add does nothing for a leaf
func (t *File) Add(child Node) {}
// Print prints the file name with leading spaces
func (t *File) Print(space int) {
for i := 0; i < space; i++ {
fmt.Print(" ")
}
fmt.Println(t.name)
}Folder can contain other Node objects. Its Add appends a child to a slice, and Print first prints the folder name, then recursively prints each child with increased indentation.
type Folder struct {
name string
child []Node
}
// Add appends a file or sub‑folder
func (t *Folder) Add(child Node) {
t.child = append(t.child, child)
}
// Print prints the folder name and then its children
func (t *Folder) Print(space int) {
for i := 0; i < space; i++ {
fmt.Print(" ")
}
fmt.Println(t.name)
space++
for _, v := range t.child {
v.Print(space)
}
}In main, a root folder representing drive D: is created, files and a sub‑folder are added, and the hierarchy is printed.
func main() {
// Root directory D:
diver := Folder{name: "D:"}
// Two files
f1 := File{name: "1.txt"}
f2 := File{name: "test.txt"}
// Sub‑folder with three files
fd1 := Folder{name: "文档"}
ff1 := File{name: "2.txt"}
ff2 := File{name: "3.txt"}
ff3 := File{name: "4.txt"}
fd1.Add(&ff1)
fd1.Add(&ff2)
fd1.Add(&ff3)
// Add files and sub‑folder to root
diver.Add(&f1)
diver.Add(&f2)
diver.Add(&fd1)
// Print the directory tree
diver.Print(0)
return
}The program outputs a hierarchical view of the simulated file system, demonstrating how the Composite pattern enables uniform treatment of individual files and composite folders.
Source code repository:
https://github.com/gofish2020/gopattern/tree/main/structure/composite
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Nullbody Notes
Go backend development, learning open-source project source code together, focusing on simplicity and practicality.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
