觀察者模式是行為型設計模式中的明星模式,廣泛應用於事件驅動系統、GUI 開發、消息通知、數據同步等場景。本文將從模式定義、角色分析、代碼構造、實戰案例、JDK 內置支持等維度,全方位解析觀察者模式。
觀察者模式定義了對象間的一種一對多依賴關係,使得每當一個對象(主題/被觀察者)改變狀態時,所有依賴於它的對象(觀察者)都會得到通知並自動更新。
別名:發布-訂閱、模型-視圖、源-監聽器、從屬者模式。推薦使用 Publisher-Subscriber 術語,清晰劃分邊界。
典型應用場景:
| 角色 | 別名 | 職責 | 實現形式 |
|---|---|---|---|
| 抽象主題 | Subject / Observable | 維護觀察者集合;提供註冊/移除/通知接口 | 接口或抽象類 |
| 抽象觀察者 | Observer | 定義更新接口 (update/operate) | 接口、抽象類或具體類 |
| 具體主題 | ConcreteSubject | 保存狀態;狀態變化時調用通知方法 | 具體類 |
| 具體觀察者 | ConcreteObserver | 持有主題引用;實現更新接口,同步狀態 | 具體類 |
addObserver()、removeObserver() 註冊管理接口update() 接收通知接口observer.update()update(state)場景:載有鈔票黃金的貨車出發,需通知特警、保安;誤將電話發給盜賊。三方均為觀察者,發送短信接口為主題。
public interface ObserverMethod {
void update(); // 拉模型:觀察者主動獲取詳情
}
// 保安
public class SecurityGuard implements ObserverMethod {
@Override public void update() {
System.out.println("車號:閩AAA888,大量現金黃金,保安提高警惕,貼身保護");
}
}
// 盜賊
public class Thief implements ObserverMethod {
@Override public void update() {
System.out.println("車號:閩AAA888,大量現金黃金,準備炸彈,大幹一票");
}
}
// 特警
public class SWAT implements ObserverMethod {
@Override public void update() {
System.out.println("車號:閩AAA888,大量現金黃金,特警提高警惕,貼身保護");
}
}
public interface Watcher {
void addWatcher(ObserverMethod watcher);
void removeWatcher(ObserverMethod watcher);
void notifyWatchers(); // 觸發通知
}
public class OperateWatcher implements Watcher {
private List observers = new ArrayList<>();
@Override
public synchronized void addWatcher(ObserverMethod watcher) {
if (watcher == null) throw new NullPointerException();
if (!observers.contains(watcher)) observers.add(watcher);
}
@Override
public void removeWatcher(ObserverMethod watcher) {
observers.remove(watcher);
}
@Override
public void notifyWatchers() {
if (observers != null) {
for (ObserverMethod observer : observers) {
if (observer != null) observer.update();
}
}
}
}
public class ObserverTest {
public static void main(String[] args) {
Watcher watcher = new OperateWatcher();
ObserverMethod guard = new SecurityGuard();
ObserverMethod thief = new Thief();
ObserverMethod swat = new SWAT();
System.out.println("=== 全部通知 ===");
watcher.addWatcher(guard);
watcher.addWatcher(thief);
watcher.addWatcher(swat);
watcher.notifyWatchers();
System.out.println("\n=== 移除盜賊後 ===");
watcher.removeWatcher(thief);
watcher.notifyWatchers();
}
}
=== 全部通知 === 車號:閩AAA888,大量現金黃金,保安提高警惕,貼身保護 車號:閩AAA888,大量現金黃金,準備炸彈,大幹一票 車號:閩AAA888,大量現金黃金,特警提高警惕,貼身保護 === 移除盜賊後 === 車號:閩AAA888,大量現金黃金,保安提高警惕,貼身保護 車號:閩AAA888,大量現金黃金,特警提高警惕,貼身保護
將具體通知內容作為參數傳遞,觀察者無需主動查詢:
// 觀察者接口擴展
public interface ObserverMethod {
void update(String message); // 推模型
}
// 主題通知時傳遞具體信息
public void notifyWatchers(String message) {
for (ObserverMethod observer : observers) {
if (observer != null) observer.update(message);
}
}
// 調用
watcher.notifyWatchers("車號:閩AAA888,大量現金黃金,請注意安保");
// 被觀察者繼承 Observable
public class WeatherData extends Observable {
private float temperature;
public void setTemperature(float temp) {
this.temperature = temp;
setChanged(); // 關鍵:標記狀態已變
notifyObservers(temp); // 推模型傳遞數據
}
}
// 觀察者實現 Observer 接口
public class CurrentDisplay implements Observer {
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
float temp = (Float) arg;
display(temp);
}
}
}
缺點:Observable 是類而非接口,限制復用;setChanged() 為 protected,需繼承才能調用;JDK 9 標記棄用,建議遷移。
public class BeanSubject {
private PropertyChangeSupport support = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void setValue(String propertyName, Object oldValue, Object newValue) {
support.firePropertyChange(propertyName, oldValue, newValue);
}
}
ApplicationEventPublisher + @EventListener,支持異步、順序、事務綁定EventBus.post(event) + @Subscribe,輕量級進程內事件總線Observable/Flowable,支持背壓、操作符鏈java.util.concurrent.Flow,標準化響應式規範| 優點 | 缺點/注意點 |
|---|---|
| 主題與觀察者鬆耦合 | 觀察者過多時通知延遲,考慮異步通知 |
| 支持廣播通信 | 循環依賴風險(A觀察B,B觀察A),需設計避免 |
| 符合開閉原則 | 觀察者異常可能影響其他觀察者,需隔離異常處理 |
| 動態註冊/移除 | 內存洩漏風險:觀察者未移除導致無法 GC,需弱引用或生命周期管理 |
觀察者模式以「一對多、鬆耦合、自動通知」解決了對象間狀態同步的核心難題。從手寫接口到 JDK Observable,再到 Spring Event、Guava EventBus、響應式流,其核心思想始終未變——發布者不關心誰訂閱,訂閱者不關心誰發布,中間由中介者解耦。掌握觀察者模式,不僅是學會一種設計模式,更是理解現代事件驅動架構、響應式編程、微服務異步通信的基石。