觀察者模式(Observer Pattern)詳解
一、觀察者模式簡介
觀察者模式(Observer Pattern) 是一種 行為型設計模式(對象行為型模式),它定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。當主題對象發生變化時,它的所有依賴者(觀察者)都會收到通知并自動更新。
你可以把它想象成“訂閱-發布”機制:當你訂閱了一份雜志后,每當有新一期出版時,你就會收到通知。這里的“你”就是觀察者,“雜志”則是被觀察的主題。
交通信號燈 <- -> 觀察目標
汽車(汽車駕駛員) <- -> 觀察者
(一對多)
軟件系統:一個對象的狀態或行為的變化將導致其他對象的狀態或行為也發生改變,它們之間將產生聯動。
觀察者模式:
- 定義了對象之間一種一對多的依賴關系,讓一個對象的改變能夠影響其他對象
- 發生改變的對象稱為觀察目標,被通知的對象稱為觀察者
- 一個觀察目標可以對應多個觀察者
別名
發布-訂閱(Publish/Subscribe)模式
模型-視圖(Model/View)模式
源-監聽器(Source/Listener)模式
從屬者(Dependents)模式
觀察者模式包含以下4個角色:
Subject(目標)
ConcreteSubject(具體目標)
Observer(觀察者)
ConcreteObserver(具體觀察者)
二、解決的問題類型
觀察者模式主要用于解決以下問題:
- 狀態變化需要通知相關聯的對象:例如,用戶界面中的模型數據發生變化時,視圖需要自動更新。需要在系統中創建一個觸發鏈。
- 避免緊耦合:允許對象之間保持松散的聯系,無需直接相互引用。一個抽象模型有兩個方面,其中一個方面依賴于另一個方面,將這兩個方面封裝在獨立的對象中使它們可以各自獨立地改變和復用。
- 實現廣播通信:可以向所有注冊的觀察者發送消息,而不需要知道它們的具體身份。
三、使用場景
場景 | 示例 |
---|---|
GUI事件處理 | 如按鈕點擊觸發窗口刷新 |
消息傳遞系統 | 如發布/訂閱模式的消息隊列 |
數據模型與視圖同步 | MVC架構中,Model數據變化時通知View更新 |
實時數據監控 | 如股票價格變動時通知所有關注的客戶端 |
四、核心概念
- Subject(主題/被觀察者):維護了一個觀察者列表,并提供方法供觀察者注冊或移除自身;在狀態改變時通知所有觀察者。
- Observer(觀察者):接收來自主題的通知并作出響應。
- ConcreteSubject & ConcreteObserver:具體實現上述兩個接口的實際類。
五、實際代碼案例(Java)
1. 定義 Observer 接口
// 觀察者接口
public interface Observer {void update(float temperature, float humidity, float pressure);
}
2. 定義 Subject 接口
import java.util.ArrayList;// 主題接口
public interface Subject {void registerObserver(Observer o); // 注冊觀察者void removeObserver(Observer o); // 移除觀察者void notifyObservers(); // 通知所有觀察者
}
3. 創建具體的 WeatherData 類作為被觀察者
public class WeatherData implements Subject {private ArrayList<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if (i >= 0) {observers.remove(i);}}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void measurementsChanged() {notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}
}
4. 創建具體的觀察者類
public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;private Subject weatherData;public CurrentConditionsDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
5. 測試代碼
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}
輸出結果:
Current conditions: 80.0F degrees and 65.0% humidity
Current conditions: 82.0F degrees and 70.0% humidity
Current conditions: 78.0F degrees and 90.0% humidity
典型代碼
典型的抽象目標類代碼
using System.Collection
abstract class Subject
{//定義一個觀察者集合用于存儲所有觀察者對象
protected ArrayList observers = new ArrayList();
//聲明抽象注冊方法,用于向觀察者集合中增加一個觀察者public abstract void Attach(Observer observer);
//聲明抽象注銷方法,用于在觀察者集合中刪除一個觀察者public abstract void Detach(Observer observer);//聲明抽象通知方法public abstract void Notify();
}
典型的具體目標類代碼
class ConcreteSubject : Subject
{public override void Attach(Observer observer){observers.Add(observer);}public override void Detach(Observer observer){observers.Remove(observer);}//實現通知方法public override void Notify(){//遍歷觀察者集合,調用每一個觀察者的響應方法foreach(object obs in observers){((Observer)obs).Update();}}
}
典型的抽象觀察者代碼
interface Observer
{void Update();
}
典型的具體觀察者代碼
class ConcreteObserver : Observer
{//實現響應方法public void Update(){//具體更新代碼}
}
典型的客戶端代碼片段
……
Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.Attach(observer);
subject.Notify();
……
其他案例
- 在某多人聯機對戰游戲中,多個玩家可以加入同一戰隊組成聯盟,當戰隊中的某一成員受到敵人攻擊時將給所有其他盟友發送通知,盟友收到通知后將做出響應。
現使用觀察者模式設計并實現該過程,以實現戰隊成員之間的聯動。
戰隊成員之間的聯動過程:
聯盟成員受到攻擊 --> 發送通知給盟友 --> 盟友做出響應
- 貓、狗與老鼠
假設貓是老鼠和狗的觀察目標,老鼠和狗是觀察者,貓叫老鼠跑,狗也跟著叫,使用觀察者模式描述該過程。
六、優缺點分析
優點 | 描述 |
---|---|
? 解耦 | 主題和觀察者之間是松散耦合的,便于獨立開發和修改。可以實現表示層和數據邏輯層的分離。 |
? 支持廣播通信 | 可以輕松地將信息廣播給多個觀察者 |
? 易于擴展 | 新增觀察者只需實現 Observer 接口即可,不影響現有代碼。符合開閉原則,增加新的具體觀察者無須修改原有系統代碼,在具體觀察者與觀察目標之間不存在關聯關系的情況下,增加新的觀察目標也很方便 |
缺點 | 描述 |
---|---|
? 可能導致性能問題 | 如果觀察者數量過多,通知過程可能會變得緩慢 |
? 潛在的內存泄漏風險 | 若未正確移除觀察者,可能導致無法釋放資源。如果存在循環依賴時可能導致系統崩潰 |
? 難以追蹤依賴關系 | 復雜的觀察者網絡可能使程序難以調試和理解 |
其他 | 沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發生變化的,而只是知道觀察目標發生了變化 |
七、與其他模式對比(補充)
模式名稱 | 目標 |
---|---|
命令模式 | 封裝請求為對象,支持請求排隊、日志記錄等操作 |
中介者模式 | 減少對象之間的直接交互,通過中介者進行協調 |
觀察者模式 | 維持對象間的一對多依賴關系,實現狀態變更的通知 |
八、最終小結
觀察者模式是一種非常實用的設計模式,特別適合那些需要在不同組件之間維持松散耦合并且能夠響應狀態變化的應用場景。通過合理地運用觀察者模式,我們可以在不破壞原有模塊獨立性的前提下,有效地實現組件間的協作與通信。
在開發圖形用戶界面(GUI)、實時數據監控系統、消息中間件等項目時,觀察者模式能夠幫助你更好地組織代碼結構,提高系統的靈活性和可維護性。
📌 一句話總結:
觀察者模式就像一個新聞通訊社,一旦有新的新聞發布,所有訂閱了該新聞的人都會立即收到通知。
部分內容由AI大模型生成,注意識別!