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

Java 8 實現觀察者模式 (上):Lambda 表達式與函數式介面重構經典模式

     編輯:Java 相關知識

前言

觀察者模式 是 GoF 23 種設計模式中最經典的行為型模式之一,定義了對象間一對多的依賴關係。Java 8 引入 Lambda 表達式與函數式介面,為觀察者模式帶來了全新的實現方式。本文將從經典實現出發,逐步重構為現代 Java 8 風格,並探討線程安全、異步通知等進階主題。

經典觀察者模式回顧

1. 核心角色

  • Subject (主題/被觀察者):維護觀察者列表,提供註冊/移除/通知方法
  • Observer (觀察者):定義更新介面 update()
  • ConcreteSubject:具體主題,狀態變化時通知觀察者
  • ConcreteObserver:具體觀察者,實現更新邏輯

2. 經典實現 (Java 7 風格)

// 主題介面
public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// 觀察者介面
public interface Observer {
    void update(String state);
}

// 具體主題
public class WeatherStation implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String weatherState;

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update(weatherState);
        }
    }

    public void setWeatherState(String state) {
        this.weatherState = state;
        notifyObservers();
    }
}

// 具體觀察者
public class PhoneDisplay implements Observer {
    @Override
    public void update(String state) {
        System.out.println("手機顯示天氣更新: " + state);
    }
}

Java 8 函數式重構

1. 觀察者介面函數式化

利用 @FunctionalInterface,單方法介面可直接用 Lambda 實例化:

@FunctionalInterface
public interface WeatherObserver {
    void onWeatherChanged(String newState);
    
    // 預設方法:支援組合
    default WeatherObserver andThen(WeatherObserver other) {
        return state -> {
            this.onWeatherChanged(state);
            other.onWeatherChanged(state);
        };
    }
}

2. 主題類現代化重構

public class WeatherStation {
    // 使用 CopyOnWriteArrayList 保證線程安全
    private final List<WeatherObserver> observers = new CopyOnWriteArrayList<>();
    private String weatherState;

    // 註冊返回註冊對象,支援後續移除
    public WeatherObserver register(WeatherObserver observer) {
        observers.add(observer);
        return observer;
    }

    // 重載:支援 Lambda 直接註冊並返回引用
    public WeatherObserver register(String id, WeatherObserver observer) {
        // 可擴展:帶 ID 註冊,便於管理
        return register(observer);
    }

    public boolean unregister(WeatherObserver observer) {
        return observers.remove(observer);
    }

    public void setWeatherState(String state) {
        this.weatherState = state;
        notifyObservers(state);
    }

    // 核心通知邏輯:使用 forEach + 方法引用
    private void notifyObservers(String state) {
        observers.forEach(observer -> {
            try {
                observer.onWeatherChanged(state);
            } catch (Exception e) {
                // 隔離異常,不影響其他觀察者
                System.err.println("觀察者通知失敗: " + e.getMessage());
            }
        });
    }
}

3. 客戶端使用示例

public class Main {
    public static void main(String[] args) {
        WeatherStation station = new WeatherStation();

        // 1. 傳統匿名內部類 (仍支援)
        station.register(new WeatherObserver() {
            @Override
            public void onWeatherChanged(String state) {
                System.out.println("【匿名類】天氣變化: " + state);
            }
        });

        // 2. Lambda 表達式 (推薦)
        WeatherObserver mobileObserver = state -> 
            System.out.println("【手機端】收到天氣: " + state);
        station.register(mobileObserver);

        // 3. 方法引用
        station.register(System.out::println); // 直接打印

        // 4. 組合觀察者 (Java 8 default 方法)
        WeatherObserver combined = mobileObserver.andThen(state -> 
            System.out.println("【日誌記錄】" + state));
        station.register("combined", combined);

        // 觸發通知
        station.setWeatherState("晴轉多雲,25°C");
        
        // 移除特定觀察者
        station.unregister(mobileObserver);
        station.setWeatherState("驟雨,18°C");
    }
}

進階模式:事件對象傳遞

1. 豐富事件對象

public class WeatherEvent {
    private final String state;
    private final long timestamp;
    private final String source;

    public WeatherEvent(String state, String source) {
        this.state = state;
        this.timestamp = System.currentTimeMillis();
        this.source = source;
    }
    // getter 省略
}

@FunctionalInterface
public interface EventObserver<T> {
    void onEvent(T event);
}

2. 泛型主題實現

public class EventSubject<T> {
    private final List<EventObserver<T>> observers = new CopyOnWriteArrayList<>();

    public void register(EventObserver<T> observer) {
        observers.add(observer);
    }

    public void unregister(EventObserver<T> observer) {
        observers.remove(observer);
    }

    public void fireEvent(T event) {
        observers.forEach(obs -> {
            try {
                obs.onEvent(event);
            } catch (Exception e) {
                // 異常處理
            }
        });
    }
}

線程安全與併發考量

1. 觀察者列表容器選擇

容器線程安全適用場景
ArrayList否單線程、註冊/通知不併發
CopyOnWriteArrayList是 (寫時複製)讀多寫少、觀察者數量較少
Collections.synchronizedList是 (鎖同步)寫操作頻繁、需強一致性
ConcurrentLinkedQueue是 (無鎖)高併發註冊/移除、允許弱一致性遍歷

2. 通知過程中的異常隔離

observers.forEach(observer -> {
    try {
        observer.onEvent(event);
    } catch (Exception e) {
        // 記錄日誌,繼續通知下一個
        log.error("Observer notification failed", e);
    }
});

3. 異步通知模式

// 使用 CompletableFuture 異步通知
public void fireEventAsync(T event) {
    observers.forEach(observer -> 
        CompletableFuture.runAsync(() -> {
            try {
                observer.onEvent(event);
            } catch (Exception e) {
                log.error("Async notification failed", e);
            }
        })
    );
}

// 或使用固定線程池
private final ExecutorService executor = Executors.newFixedThreadPool(4);

public void fireEventAsyncPooled(T event) {
    observers.forEach(observer -> 
        executor.submit(() -> {
            try {
                observer.onEvent(event);
            } catch (Exception e) {
                log.error("Pooled async notification failed", e);
            }
        })
    );
}

命名規範最佳實踐

遵循 Java 標準事件命名慣例:

  • 觀察者介面:*Listener 或 *Observer (如 ActionListener, WeatherObserver)
  • 註冊方法:add*Listener / register*Observer
  • 移除方法:remove*Listener / unregister*Observer
  • 通知方法:fire*Event / notify*Observers
  • 回調方法:on*Event / *Changed (如 onWeatherChanged, actionPerformed)

總結 (上)

Java 8 通過 Lambda 表達式、函數式介面、預設方法、方法引用等特性,極大簡化了觀察者模式的實現:

  1. 語法簡潔:單方法介面可直接用 Lambda 替代匿名內部類
  2. 組合靈活:default 方法支援觀察者鏈式組合
  3. 類型安全:泛型事件主題提供編譯期檢查
  4. 線程安全:CopyOnWriteArrayList 等並發容器開箱即用
  5. 異步支援:CompletableFuture 輕鬆實現非阻塞通知

下篇將深入探討:反應式流 (Reactive Streams) 整合、背壓處理、分佈式觀察者模式、以及與 Spring ApplicationEvent、RxJava、Project Reactor 的對比整合。

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