定義一對多的依賴:一個 subject 狀態改變時,自動通知並更新所有註冊的 observer。核心是讓「被觀察方」不必知道觀察它的是誰、有幾個,只負責廣播事件。
🧠 When to Use
TIP
訊號:一份資料變動時,有「一群」對象要連動更新(UI 重繪、快取失效、發通知),而且這群對象的數量與身分會隨執行期增減。
- 一個狀態的改變需要連動更新多個且不固定的對象。
- 想讓 subject 與 observer 鬆耦合——subject 只認得抽象介面,不認得具體型別。
- 需要在執行期動態訂閱 / 退訂事件。
⚖️ Structure & Variants
經典版 — Subject 維護清單,狀態變就 notify
fun interface Observer { fun update(temp: Int) }
class WeatherStation {
private val observers = mutableListOf<Observer>()
fun subscribe(o: Observer) { observers += o }
fun unsubscribe(o: Observer) { observers -= o }
var temperature: Int = 0
set(value) {
field = value
observers.forEach { it.update(value) } // ← 廣播
}
}
val station = WeatherStation()
station.subscribe { temp -> println("Display: $temp°C") }
station.temperature = 26 // Display: 26°CWeatherStation不知道有幾個顯示器;退訂只是把物件移出清單。
Push vs Pull — 通知時帶多少資料
- Push:
update(value)直接把變動內容塞給 observer,簡單但耦合到參數形狀。 - Pull:只發「我變了」的訊號,observer 再回頭呼叫 subject 的 getter 拿需要的部分,較鬆耦合但多一次往返。
和 Pub/Sub、Reactive Stream 的關係
- Pub/Sub 通常多一層 event channel/broker,發布者與訂閱者彼此完全不知道對方,比 Observer 更解耦。
- FP 的 reactive stream(RxJava、Flow)可視為 Observer 的進化:把「事件序列」變成可組合的一等公民,內建 map/filter/背壓。
⚠️ Misuse & Anti-patterns
- 忘記退訂造成記憶體洩漏:observer 被 subject 的清單長期持有,無法被 GC(Lapsed Listener)。要有對稱的 unsubscribe 或弱引用。
- 通知串成連鎖 / 循環更新:A 通知 B、B 改了又通知 A,造成無窮迴圈或難追的更新風暴。
- 在 update 裡放重邏輯:同步廣播會讓一次 setter 拖很久;考慮非同步或批次合併通知。
🔑 Takeaways
- 本質是「subject 廣播、observer 訂閱」的一對多鬆耦合,subject 不認得具體 observer。
- Push 簡單、Pull 解耦;依資料量與耦合度取捨。
- 最大坑是退訂與記憶體洩漏;要連動的事件多時,直接上 reactive stream 更省心。
No notes yet — jot your takeaways or Q&A here.