โ Back to Design Patterns
๐ณ
Structural
Composite
Composes objects into tree structures and lets clients treat individual objects and compositions uniformly.
The Problem
You have a tree hierarchy (UI components, file system, org chart) where operations should work the same way on both leaf nodes and containers, without special-casing in client code.
How It Works
- โบLeaf and Composite share the same interface โ clients need no instanceof checks
- โบRecursive structure: containers can hold both leaves and other containers
- โบSimplifies client code that works with tree structures
- โบEasy to add new kinds of components without changing client code
- โบMay be overly general when you need restrictions on what can compose what
When to Use
โUI component trees, file system trees, org charts, menu hierarchies
โWhen client code should treat individual items and groups identically
โAny recursive part-whole hierarchy
โRendering nested components uniformly
Real-World Examples
๐
iOS
UIView hierarchy (every UIView can contain child views), SwiftUI View tree
๐ค
Android
ViewGroup containing Views and other ViewGroups, Jetpack Compose UI tree
๐
Web
DOM tree, React component tree, virtual DOM diffing
Code Examples
Swift
protocol UIComponent {
var name: String { get }
func render(indent: String)
}
class UILeaf: UIComponent {
let name: String
init(_ name: String) { self.name = name }
func render(indent: String = "") { print("\(indent)[\(name)]") }
}
class UIContainer: UIComponent {
let name: String
private var children = [UIComponent]()
init(_ name: String) { self.name = name }
func add(_ child: UIComponent) { children.append(child) }
func render(indent: String = "") {
print("\(indent)<\(name)>")
children.forEach { $0.render(indent: indent + " ") }
}
}
let screen = UIContainer("Screen")
let header = UIContainer("Header")
header.add(UILeaf("Logo")); header.add(UILeaf("NavTitle"))
screen.add(header)
screen.add(UILeaf("HeroImage"))
screen.add(UILeaf("CTAButton"))
screen.render()
// <Screen>
// <Header>
// [Logo]
// [NavTitle]
// [HeroImage]
// [CTAButton]