首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 觀察者模式演進全紀錄:從手寫實現到 Guava EventBus 架構思維

     編輯:Java 程式設計入門

觀察者模式是行為型設計模式的基石,定義了物件間「一對多」的依賴關係,當主題狀態變更時自動通知所有觀察者。本文以演進式視角,從最基礎的手寫實現、多事件解耦、JDK 內建 API、Quartz 實戰風格,層層遞進至 Guava EventBus 的現代化架構思維,完整呈現觀察者模式在工程實踐中的成長路徑。

階段一:最簡單的觀察者模式(手寫核心骨架)

核心角色:

  • Subject (IObservable):維護觀察者列表,提供註冊/移除/通知介面。
  • Observer (IObserver):定義 update(Subject) 回調方法。
// 核心介面
interface IObservable {
    void registerObserver(IObserver o);
    void unregisterObserver(IObserver o);
    void notifyObservers();
    String getState();
    void changeState();
    void close();
}

interface IObserver {
    void update(IObservable observable);  // 直接傳遞主題引用
}

// 具體實現
class Observable implements IObservable {
    private final List observers = Collections.synchronizedList(new ArrayList<>());
    private String state = "NEW";

    public void registerObserver(IObserver o) { observers.add(o); }
    public void unregisterObserver(IObserver o) { observers.remove(o); }

    public void notifyObservers() {
        for (IObserver o : observers) o.update(this);  // 遍歷通知
    }
    // ... 狀態變更時呼叫 notifyObservers()
}

class Observer implements IObserver {
    private final String name;
    public Observer(String name) { this.name = name; }
    public void update(IObservable obs) {
        System.out.println(name + " 收到狀態變更: " + obs.getState());
    }
}

缺陷:觀察者強耦合具體 Subject,且所有觀察者收到相同通知,無法針對特定事件訂閱,違反「介面隔離原則」。

階段二:多事件觀察者模式(引入 Event 物件解耦)

引入 Event 物件 作為通知載體,Subject 維護 Map>,觀察者針對特定事件註冊。


interface IEvent {
    void eventChange();
    String getState();
}

class EventA implements IEvent { /* 狀態: INITIALIZED -> PENDING */ }
class EventB implements IEvent { /* 狀態: NEW -> IDLE */ }

interface IObservable {
    void registerObserver(IEvent event, IObserver observer);
    void unregisterObserver(IEvent event, IObserver observer);
    void notifyObservers(IEvent event);
    IEvent getEventA();
    IEvent getEventB();
    void changeStateA();  // 觸發 EventA
    void changeStateB();  // 觸發 EventB
}

class Observable implements IObservable {
    private final Map> eventObservers = new Hashtable<>();
    private final EventA eventA = new EventA();
    private final EventB eventB = new EventB();

    public void registerObserver(IEvent event, IObserver o) {
        eventObservers.computeIfAbsent(event, k -> Collections.synchronizedSet(new HashSet<>())).add(o);
    }
    public void notifyObservers(IEvent event) {
        Set obs = eventObservers.get(event);
        if (obs != null) obs.forEach(o -> o.update(event));  // 傳遞 Event 而非 Subject
    }
    // ...
}

interface IObserver {
    void update(IEvent event);  // 參數改為 Event
}

進步:觀察者只關心訂閱的事件,資訊隱藏得到改善。
殘留問題:事件類型硬編碼在 Subject 介面中(getEventA()、changeStateA()),新增事件需修改介面與實現類,違反「開閉原則」。

階段三:引入事件總線——徹底解耦(Guava EventBus 思想)

核心洞察:將「事件分發」職責從 Subject 移至獨立的 EventBus 元件。Subject 只負責發布事件,觀察者只負責訂閱感興趣事件,兩者完全不知彼此存在。


// Guava EventBus 風格簡化示意
class EventBus {
    private final Map, Set> subscribers = new ConcurrentHashMap<>();

    public void register(Object listener) {
        // 掃描 @Subscribe 標註的方法,按參數類型(事件類型)分組註冊
    }
    public void post(Object event) {
        // 根據事件類型查找訂閱者,同步/異步調用
    }
}

// 使用範例
class OrderService {
    private final EventBus eventBus;
    public OrderService(EventBus bus) { this.eventBus = bus; }
    public void createOrder(Order order) {
        // 業務邏輯...
        eventBus.post(new OrderCreatedEvent(order));  // 發布事件,無感知觀察者
    }
}

class NotificationListener {
    @Subscribe
    public void onOrderCreated(OrderCreatedEvent e) { /* 發送通知 */ }
}

優勢:
✅ 完全解耦:發布者/訂閱者零依賴
✅ 類型安全:基於事件類型自動路由
✅ 支援異步/同步、死信隊列、事件繼承多態訂閱
✅ 符合「單一職責」與「開閉原則」

階段四:JDK 內建 Observable/Observer(已棄用,僅供理解)

java.util.Observable (類) + java.util.Observer (介面),Java 9 標記 @Deprecated,原因:
❌ Observable 是類而非介面,限制繼承
❌ 同步通知、無異步支援
❌ 觀察者順序不可控、無事件類型區分

階段五:Quartz SchedulerListener 實戰風格(領域特化觀察者)

Quartz 針對排程領域定義 SchedulerListener 介面,僅包含 jobScheduled(Trigger)、jobUnScheduled(Trigger) 兩個回調,QuartzScheduler 內部維護 List,手動同步管理。展示了「領域驅動設計」中為特定業務場景裁剪觀察者介面的實踐。

總結:觀察者模式選型指南

場景推薦方案核心理由
簡單單一事件、極少觀察者、無擴展需求手寫基礎版 / JDK Observable(舊專案)零依賴、極簡
多事件類型、觀察者關注點不同、需資訊隱藏多事件 Map 版 / 手寫 EventBus事件粒度控制、介面隔離
企業級應用、微服務內部事件驅動、需異步/重試/順序保證Guava EventBus / Spring ApplicationEvent / Kafka/RocketMQ生產級特性、生態完善、架構解耦
跨進程/分散式事件通知消息隊列 (Kafka, RabbitMQ, Pulsar)持久化、高可用、擴展性

架構感悟:從手寫 List 到 Guava EventBus,本質是「職責分離」的不斷深化——將「誰關心什麼事件」與「事件如何分發」從業務邏輯中剝離,交給專門的基礎設施元件。這正是現代框架(Spring Events、Netty ChannelPipeline、Vert.x EventBus)的核心設計哲學。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved