โ† 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]