Design Patterns adalah solusi yang sudah teruji untuk masalah desain peranti lunak yang berulang. Buku klasik Gang of Four (GoF) mengkatalogkan 23 pola ke dalam tiga kelompok: creational (pembuatan object), structural (komposisi object), dan behavioral (interaksi antar object). Pada chapter ini kita akan melihat implementasi idiomatic Kotlin untuk beberapa pola perwakilan dari setiap kelompok. Pembaca akan melihat bagaimana fitur Kotlin seperti object, data class, by delegation, dan function type menggantikan boilerplate Java tradisional.

Tiga Kelompok Pola #

KelompokTujuanContoh pola
CreationalMembuat object dengan fleksibelSingleton, Factory Method, Builder
StructuralMenyusun object menjadi struktur lebih besarAdapter, Decorator, Facade
BehavioralMengatur interaksi antar objectStrategy, Observer, Command, Template Method

Kotlin menawarkan sintaks yang lebih ringkas untuk banyak pola. Sebagai contoh, Singleton menjadi cukup object, dan Strategy seringkali cukup dengan function type (T) -> R tanpa perlu interface terpisah.

Implementasi Beberapa Pola Idiomatic #

kotlin
// Demo Design Patterns: overview GoF patterns dengan implementasi Kotlin idiomatic.
// Patterns: Creational, Structural, Behavioral.

// === CREATIONAL PATTERNS ===

// Singleton (Kotlin object)
object KonfigurasiApp {
    val versi = "1.0.0"
    val nama = "Kotlin Demo"
}

// Factory Method (companion object sebagai factory)
interface Hewan { fun bunyi(): String }
class Kucing : Hewan { override fun bunyi() = "meong" }
class Anjing : Hewan { override fun bunyi() = "guk" }

class HewanFactory {
    companion object {
        fun buat(jenis: String): Hewan = when (jenis.lowercase()) {
            "kucing" -> Kucing()
            "anjing" -> Anjing()
            else -> throw IllegalArgumentException("Jenis tidak dikenal: $jenis")
        }
    }
}

// Builder (idiomatic Kotlin: data class + copy + apply)
data class KonfigurasiServer(
    val host: String = "localhost",
    val port: Int = 8080,
    val timeout: Int = 30,
    val ssl: Boolean = false
)

// === STRUCTURAL PATTERNS ===

// Adapter
interface Target { fun request(): String }
class Adaptee { fun spesifikRequest() = "respons spesifik adaptee" }
class Adapter(private val adaptee: Adaptee) : Target {
    override fun request() = adaptee.spesifikRequest().uppercase()
}

// Decorator (via `by` delegation)
interface Notifier { fun kirim(pesan: String) }
class EmailNotifier : Notifier {
    override fun kirim(pesan: String) = println("[email] $pesan")
}
class LoggingNotifier(private val inner: Notifier) : Notifier by inner {
    override fun kirim(pesan: String) {
        println("[log] akan mengirim notifikasi")
        inner.kirim(pesan)
        println("[log] notifikasi terkirim")
    }
}

// Facade
class CPU { fun nyalakan() = "CPU on" }
class Memory { fun load() = "Memory loaded" }
class Disk { fun read() = "Disk read" }
class ComputerFacade {
    private val cpu = CPU()
    private val mem = Memory()
    private val disk = Disk()
    fun start(): String = "${cpu.nyalakan()}, ${mem.load()}, ${disk.read()}"
}

// === BEHAVIORAL PATTERNS ===

// Strategy (function as parameter — idiomatic Kotlin)
fun proses(data: List<Int>, strategi: (List<Int>) -> Int): Int = strategi(data)
val strategiSum: (List<Int>) -> Int = { it.sum() }
val strategiMax: (List<Int>) -> Int = { it.maxOrNull() ?: 0 }

// Observer (via StateFlow-like simple impl)
class Publisher<T> {
    private val subscribers = mutableListOf<(T) -> Unit>()
    fun subscribe(s: (T) -> Unit) = subscribers.add(s)
    fun publish(value: T) = subscribers.forEach { it(value) }
}

// Command
interface Command { fun execute(): String }
class CommandA : Command { override fun execute() = "Command A dieksekusi" }
class CommandB : Command { override fun execute() = "Command B dieksekusi" }
class Invoker {
    private val history = mutableListOf<Command>()
    fun run(c: Command): String {
        history.add(c)
        return c.execute()
    }
}

// Template Method
abstract class PesananTemplate {
    fun proses(): String {
        val a = validasi()
        val b = bayar()
        val c = kirim()
        return "$a -> $b -> $c"
    }
    abstract fun validasi(): String
    abstract fun bayar(): String
    abstract fun kirim(): String
}
class PesananDigital : PesananTemplate() {
    override fun validasi() = "validasi digital"
    override fun bayar() = "bayar digital"
    override fun kirim() = "kirim email"
}

fun main() {
    println("=== Demo Design Patterns ===")

    // Creational
    println("KonfigurasiApp: ${KonfigurasiApp.nama} v${KonfigurasiApp.versi}")
    println("Factory: ${HewanFactory.buat("kucing").bunyi()}")
    val cfg = KonfigurasiServer().copy(host = "production.example.com", ssl = true)
    println("Builder copy: $cfg")

    println()
    // Structural
    val adapter = Adapter(Adaptee())
    println("Adapter: ${adapter.request()}")
    println("--- Decorator ---")
    val notifier: Notifier = LoggingNotifier(EmailNotifier())
    notifier.kirim("halo")
    println("Facade: ${ComputerFacade().start()}")

    println()
    // Behavioral
    val data = listOf(1, 2, 3, 4, 5)
    println("Strategy sum: ${proses(data, strategiSum)}")
    println("Strategy max: ${proses(data, strategiMax)}")
    println("--- Observer ---")
    val pub = Publisher<String>()
    pub.subscribe { println("  Sub 1 menerima: $it") }
    pub.subscribe { println("  Sub 2 menerima: $it") }
    pub.publish("event")
    println("--- Command ---")
    val inv = Invoker()
    println(inv.run(CommandA()))
    println(inv.run(CommandB()))
    println("--- Template Method ---")
    println(PesananDigital().proses())
}

Output yang Dihasilkan:

text
=== Demo Design Patterns ===
KonfigurasiApp: Kotlin Demo v1.0.0
Factory: meong
Builder copy: KonfigurasiServer(host=production.example.com, port=8080, timeout=30, ssl=true)

Adapter: RESPONS SPESIFIK ADAPTEE
--- Decorator ---
[log] akan mengirim notifikasi
[email] halo
[log] notifikasi terkirim
Facade: CPU on, Memory loaded, Disk read

Strategy sum: 15
Strategy max: 5
--- Observer ---
  Sub 1 menerima: event
  Sub 2 menerima: event
--- Command ---
Command A dieksekusi
Command B dieksekusi
--- Template Method ---
validasi digital -> bayar digital -> kirim email

Bedah Kode Baris-per-Baris #

Mari kita amati bagaimana Kotlin mempersingkat banyak pola:

  1. Baris 7-10 (object KonfigurasiApp): Singleton tradisional Java membutuhkan private constructor dan field INSTANCE dengan getInstance(). Kotlin cukup object; kompilator membuat instance tunggal secara otomatis.
  2. Baris 13-25 (HewanFactory): Companion object menjadi wadah factory method. when dengan else branch yang melempar exception memberikan jalur yang jelas untuk input tidak dikenal.
  3. Baris 28-34 (KonfigurasiServer): Builder Java membutuhkan class terpisah dengan banyak setter. Kotlin cukup data class dengan default parameter dan copy() untuk membuat instance baru dengan field yang diperbarui.
  4. Baris 49-55 (LoggingNotifier): Decorator tradisional Java harus mendelegasikan setiap method secara manual. Kotlin by inner mendelegasikan seluruh interface secara otomatis; kita hanya override method yang ingin ditambah behavior.
  5. Baris 71-72 (fun proses): Strategy tradisional memerlukan interface Strategi dan beberapa class concrete. Kotlin cukup function type (List<Int>) -> Int; lambda dapat diteruskan langsung sebagai parameter.

Kapan Tidak Memakai Pola #

Pola adalah solusi, tetapi bukan tujuan. Memaksakan pola pada masalah sederhana hanya menambah kompleksitas tanpa manfaat. Sebelum menerapkan pola, ajukan pertanyaan: apakah masalahnya benar-benar berulang? Apakah kompleksitas pola sepadan dengan fleksibilitas yang diperoleh? Banyak kode yang sehat tanpa pola formal selama tetap mengikuti prinsip SOLID.

Penutup #

Katalog GoF tetap relevan, tetapi implementasinya di Kotlin seringkali lebih ringkas berkat fitur bahasa modern. Pada chapter-chapter berikutnya kita akan membedah setiap pola secara mendalam, mulai dari Factory Pattern yang memanfaatkan companion object sebagai factory.

Referensi #

Referensi Lanjutan #

  • Kotlin documentation home — halaman utama dokumentasi Kotlin yang merangkum object, data class, dan interface sebagai pondasi design pattern