โ Kembali ke Design Pattern
๐๏ธ
Behavioral
Observer
Mendefinisikan ketergantungan one-to-many sehingga saat state satu objek berubah, semua objek yang bergantung padanya otomatis diberi tahu.
Masalahnya
Beberapa bagian aplikasi perlu bereaksi saat data bersama berubah, tetapi kamu tidak ingin sumber datanya mengenal atau terikat erat dengan para konsumennya.
Cara Kerja
- โบSubject (publisher) menyimpan daftar observer (subscriber)
- โบLoose coupling: subject tidak mengenal class observer yang konkret
- โบModel push mengirim data bersama notifikasi; model pull membiarkan observer mengambilnya sendiri
- โบBerisiko memory leak jika observer tidak di-unsubscribe (gunakan weak reference)
- โบDikenal juga sebagai pattern Publish/Subscribe atau Event Listener
Kapan Digunakan
โUI yang harus diperbarui saat data di baliknya berubah (data binding)
โSistem event di mana beberapa komponen bereaksi terhadap event yang sama
โSistem notifikasi, analytics tracking, logging ke banyak target
โFondasi reactive programming (Combine, RxSwift, LiveData)
Contoh di Dunia Nyata
๐
iOS
NotificationCenter, Combine Publisher/Subscriber, KVO, @Published + @ObservedObject
๐ค
Android
LiveData.observe(), StateFlow.collect(), RxJava Observable, EventBus
๐
Web
EventEmitter (Node.js), RxJS Observable, Vue reactivity, React useEffect
Contoh Kode
Swift
protocol StoreObserver: AnyObject {
func storeDidUpdate<T>(_ newValue: T)
}
class Store<T> {
private var observers = [WeakBox]()
var value: T { didSet { notify(value) } }
init(_ value: T) { self.value = value }
func subscribe(_ observer: StoreObserver) {
observers.append(WeakBox(observer))
}
private func notify(_ newValue: T) {
observers.forEach { $0.value?.storeDidUpdate(newValue) }
}
private class WeakBox {
weak var value: StoreObserver?
init(_ v: StoreObserver) { value = v }
}
}
class CounterLabel: StoreObserver {
func storeDidUpdate<T>(_ newValue: T) {
print("Label updated: \(newValue)")
}
}
let store = Store(0)
let label = CounterLabel()
store.subscribe(label)
store.value = 5 // "Label updated: 5"
store.value = 10 // "Label updated: 10"