โ† Kembali ke Design Pattern
๐ŸŒณ
Structural

Composite

Menyusun objek menjadi struktur tree dan membuat client bisa memperlakukan objek tunggal maupun gabungannya dengan cara yang sama.

Masalahnya

Kamu punya hierarki tree (komponen UI, file system, bagan organisasi) di mana operasi harus bekerja sama persis pada leaf maupun container, tanpa kasus khusus di kode client.

Cara Kerja

  • โ€บLeaf dan Composite berbagi interface yang sama, client tidak perlu pengecekan instanceof
  • โ€บStruktur rekursif: container bisa berisi leaf maupun container lain
  • โ€บMenyederhanakan kode client yang bekerja dengan struktur tree
  • โ€บMudah menambahkan jenis komponen baru tanpa mengubah kode client
  • โ€บBisa terlalu umum saat kamu butuh batasan tentang apa yang boleh menyusun apa

Kapan Digunakan

โœ“Tree komponen UI, tree file system, bagan organisasi, hierarki menu
โœ“Saat kode client harus memperlakukan item tunggal dan grup secara identik
โœ“Hierarki part-whole apa pun yang bersifat rekursif
โœ“Me-render komponen bersarang secara seragam

Contoh di Dunia Nyata

๐ŸŽ
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

Contoh Kode

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]