摘要
本文詳細介紹了觀察者設計模式,包括其定義、結構、實現方式、適用場景以及實戰示例。通過代碼示例展示了如何在Spring框架下實現觀察者模式,以及如何通過該模式實現狀態變化通知。同時,對比了觀察者模式與消息中間件在設計理念、耦合程度、通信方式和分布式支持等方面的差異,幫助讀者更好地理解和選擇合適的實現方式。
1. 觀察者設計模式定義
觀察者設計模式(Observer Pattern)是一種行為型設計模式,定義了一種一對多的依賴關系,使得當一個對象(被觀察者)的狀態發生改變時,所有依賴它的對象(觀察者)都會得到自動通知并更新,從而實現對象間的松耦合和實時通訊。觀察者模式通過定義對象間的發布-訂閱關系,實現事件的自動通知和響應,適合事件驅動和異步消息場景。
1.1. 核心定義
角色
- 被觀察者(Subject):維護一組觀察者,負責狀態的管理和通知。
- 觀察者(Observer):注冊到被觀察者,一旦被觀察者狀態變化,接收通知并執行相應動作。
目的:讓多個觀察者對象自動獲得被觀察者的狀態變化,降低對象間耦合度。
2. 觀察者設計模式結構
觀察者模式包含如下角色:
- Subject: 目標
- ConcreteSubject: 具體目標
- Observer: 觀察者
- ConcreteObserver: 具體觀察者
2.1. 觀察者模式類圖
2.2. 觀察者模式時序圖
3. 觀察者設計模式實現方式
3.1. 觀察者設計模式實現步驟
3.1.1. 定義抽象主題(Subject)接口
- 提供注冊、移除和通知觀察者的方法。
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}
3.1.2. 定義抽象觀察者(Observer)接口
- 聲明一個更新方法,主題狀態變化時調用。
public interface Observer {void update(String message);
}
3.1.3. 具體主題類實現 Subject
- 維護觀察者列表,實現注冊、移除、通知邏輯。
- 當自身狀態變化時調用
notifyObservers()
,遍歷通知所有觀察者。
import java.util.ArrayList;
import java.util.List;public class ConcreteSubject implements Subject {private final List<Observer> observers = new ArrayList<>();private String state;@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(state);}}public void setState(String state) {this.state = state;notifyObservers();}
}
3.1.4. 具體觀察者實現 Observer
- 實現
update
方法,接收通知并做出響應。
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " 收到通知,狀態變為: " + message);}
}
3.2. 觀察者測試示例
public class ObserverPatternDemo {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver("觀察者1");Observer observer2 = new ConcreteObserver("觀察者2");subject.registerObserver(observer1);subject.registerObserver(observer2);subject.setState("狀態1");subject.setState("狀態2");}
}
說明
- 主題(Subject)維護觀察者集合,狀態變化時通知所有觀察者。
- 觀察者實現更新接口,獲得主題最新狀態。
4. 觀察者設計模式適合場景
4.1. ? 適合使用觀察者設計模式的場景
適合場景 | 說明 |
事件驅動系統 | 事件產生后需要通知多個模塊或對象做出響應,如 UI 事件監聽、消息推送。 |
一對多依賴關系 | 一個對象狀態變化需要通知多個依賴對象,如股票價格更新通知所有訂閱者。 |
松耦合需求 | 希望對象間解耦,觀察者與被觀察者不直接依賴,實現靈活擴展。 |
廣播通信 | 需要將消息廣播給多個接收者,且接收者可動態增加和移除。 |
異步通知 | 狀態更新后,通知觀察者進行異步處理。 |
分布式系統 | 多個服務或模塊之間的狀態同步與消息推送。 |
4.2. ? 不適合使用觀察者設計模式的場景
不適合場景 | 說明 |
對象之間不存在依賴關系 | 彼此獨立的對象無需相互通知。 |
通知對象數量固定且簡單 | 只有少數固定對象,且耦合關系明確,使用簡單調用即可。 |
需要嚴格同步控制的場景 | 觀察者通知是異步的,無法滿足嚴格同步時序需求。 |
性能敏感場景 | 大量觀察者通知導致性能開銷大,影響系統響應速度。 |
過度使用導致復雜性增加 | 觀察者鏈條過長,維護困難,代碼難以理解。 |
總結:觀察者模式主要適用于需要“自動廣播狀態變化給多個對象”且“希望對象間低耦合”的場景。不適合簡單調用、同步嚴格或性能瓶頸明顯的場景。
5. 觀察者設計模式實戰示例
5.1. 場景說明
在風控系統中,當訂單狀態發生變化(比如風控審核結果出來后),需要通知多個模塊(比如日志記錄模塊、報警模塊、緩存更新模塊)來響應該狀態變化。
5.2. 定義觀察者接口
public interface OrderStatusObserver {void update(String orderId, String status);
}
5.3. 定義具體觀察者實現類
import org.springframework.stereotype.Component;@Component
public class LoggingObserver implements OrderStatusObserver {@Overridepublic void update(String orderId, String status) {System.out.println("日志模塊:訂單 " + orderId + " 狀態更新為:" + status);// 這里可以寫日志持久化操作}
}@Component
public class AlertObserver implements OrderStatusObserver {@Overridepublic void update(String orderId, String status) {if ("REJECTED".equals(status)) {System.out.println("報警模塊:訂單 " + orderId + " 審核拒絕,觸發報警!");// 觸發報警邏輯}}
}@Component
public class CacheObserver implements OrderStatusObserver {@Overridepublic void update(String orderId, String status) {System.out.println("緩存模塊:更新訂單 " + orderId + " 狀態緩存為:" + status);// 緩存刷新邏輯}
}
5.4. 定義主題接口和實現類
public interface OrderStatusSubject {void registerObserver(OrderStatusObserver observer);void removeObserver(OrderStatusObserver observer);void notifyObservers(String orderId, String status);
}
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;@Component
public class OrderStatusSubjectImpl implements OrderStatusSubject {private final List<OrderStatusObserver> observers = new ArrayList<>();// 使用Spring自動注入所有實現了OrderStatusObserver接口的Bean@Autowiredprivate List<OrderStatusObserver> observerBeans;@PostConstructpublic void init() {// 初始化時,將所有觀察者注冊進列表observers.addAll(observerBeans);}@Overridepublic void registerObserver(OrderStatusObserver observer) {observers.add(observer);}@Overridepublic void removeObserver(OrderStatusObserver observer) {observers.remove(observer);}@Overridepublic void notifyObservers(String orderId, String status) {for (OrderStatusObserver observer : observers) {observer.update(orderId, status);}}
}
5.5. 4. 業務調用示例(比如風控服務)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class RiskControlService {@Autowiredprivate OrderStatusSubject orderStatusSubject;public void auditOrder(String orderId, boolean passed) {// 風控業務邏輯String status = passed ? "APPROVED" : "REJECTED";System.out.println("風控審核完成,訂單:" + orderId + ",狀態:" + status);// 審核完成后,通知所有觀察者orderStatusSubject.notifyObservers(orderId, status);}
}
5.6. 測試Controller(可選)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RiskController {@Autowiredprivate RiskControlService riskControlService;@PostMapping("/auditOrder")public String auditOrder(@RequestParam String orderId, @RequestParam boolean passed) {riskControlService.auditOrder(orderId, passed);return "審核完成";}
}
5.7. 實戰示例總結
- 觀察者接口
OrderStatusObserver
,多個實現類由Spring管理并注入。 - 主題
OrderStatusSubjectImpl
維護觀察者列表,利用@Autowired
自動注入所有觀察者Bean。 - 風控業務執行后調用主題的
notifyObservers()
,實現狀態變化通知。 - 全部采用Spring注解管理,未用構造函數注入,代碼簡潔且易于擴展。
6. 觀察者設計模式思考
6.1. 觀察著模式和使用消息中間件來通知處理的方式有什么不同?
很好的問題!觀察者模式和消息中間件在實現“發布-訂閱”或通知機制時都很常用,但它們的設計理念、實現方式和適用場景有顯著差別。下面幫你詳細分析兩者的區別:
維度 | 觀察者模式 | 消息中間件(如Kafka、RabbitMQ、ActiveMQ等) |
設計層次 | 設計模式,屬于應用內部的設計范式,通常是在代碼內部直接實現。 | 獨立的中間件系統,屬于基礎設施層,提供異步消息傳遞機制。 |
耦合程度 | 被觀察者和觀察者耦合較緊密,觀察者必須知道被觀察者實例(盡管解耦設計可以降低耦合)。 | 發布者和訂閱者完全解耦,彼此不直接依賴,只通過消息中間件通信。 |
通信方式 | 通常是同步調用,狀態變化時立即調用觀察者的更新方法。 | 典型異步通信,消息發送后,接收方異步消費,支持異步處理和緩沖。 |
分布式支持 | 一般用于單體應用或同一JVM內對象之間的通知。 | 天生支持分布式系統,跨進程、跨服務器,甚至跨數據中心的消息傳遞。 |
擴展性 | 觀察者數量有限,擴展時需要修改代碼或依賴注入配置。 | 高度擴展,支持大量消費者和復雜路由規則,方便動態添加消費者。 |
容錯與持久化 | 無持久化和容錯機制,一旦通知失敗可能丟失信息。 | 支持消息持久化、重試、消息確認、死信隊列,增強系統可靠性。 |
性能影響 | 觀察者調用是同步的,影響調用鏈的執行時間,可能導致性能瓶頸。 | 異步處理,解耦主業務流程,提升系統吞吐量和響應速度。 |
使用場景 | 小型系統、UI事件響應、簡單狀態同步、模塊內部事件通知。 | 大型分布式系統、微服務間異步通信、異步任務調度、事件驅動架構。 |
6.1.1. 簡單總結:
- 觀察者模式 適合應用內、輕量級的同步通知,設計簡單,適合對象之間狀態變化通知。
- 消息中間件 適合跨進程、跨網絡、異步且高可靠的消息通信,適合分布式系統異步解耦和復雜事件流轉。
6.1.2. 舉例對比:
- 觀察者模式:風控模塊風控結果出來后,同步通知日志模塊和報警模塊,立即處理。
- 消息中間件:風控模塊發布“風控結果”消息到消息隊列,日志模塊和報警模塊異步訂閱該消息,消息隊列保證消息可靠送達和解耦。
博文參考
- 3. 觀察者模式 — Graphic Design Patterns
- 觀察者設計模式