溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java的觀察者模式是什么

發布時間:2021-06-18 09:15:23 來源:億速云 閱讀:156 作者:chen 欄目:web開發
# Java的觀察者模式是什么

## 目錄
1. [觀察者模式概述](#觀察者模式概述)
2. [模式結構與角色](#模式結構與角色)
3. [Java內置觀察者實現](#java內置觀察者實現)
4. [自定義觀察者模式實現](#自定義觀察者模式實現)
5. [推模型與拉模型](#推模型與拉模型)
6. [觀察者模式優缺點](#觀察者模式優缺點)
7. [典型應用場景](#典型應用場景)
8. [與其他模式的關系](#與其他模式的關系)
9. [JDK中的觀察者模式](#jdk中的觀察者模式)
10. [Spring框架中的應用](#spring框架中的應用)
11. [實際案例演示](#實際案例演示)
12. [常見問題與解決方案](#常見問題與解決方案)
13. [觀察者模式變體](#觀察者模式變體)
14. [性能考量](#性能考量)
15. [總結](#總結)

<a id="觀察者模式概述"></a>
## 1. 觀察者模式概述

觀察者模式(Observer Pattern)是一種行為設計模式,它定義了對象之間的一對多依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都會得到通知并自動更新。這種模式也被稱為發布-訂閱(Publish-Subscribe)模式。

### 1.1 基本概念

在觀察者模式中,有兩個主要角色:
- **Subject(主題)**:被觀察的對象,它維護一組觀察者,提供添加和刪除觀察者的方法,并在狀態改變時通知觀察者
- **Observer(觀察者)**:定義了一個更新接口,用于在主題狀態改變時接收通知

### 1.2 模式價值

觀察者模式的核心價值在于:
- 解耦主題和觀察者,使它們可以獨立變化
- 支持廣播通信,主題無需知道具體有哪些觀察者
- 遵循開放-封閉原則,可以隨時增加新的觀察者

<a id="模式結構與角色"></a>
## 2. 模式結構與角色

### 2.1 UML類圖

```plantuml
@startuml
class Subject {
    +attach(Observer)
    +detach(Observer)
    +notify()
}

class Observer {
    +update()
}

class ConcreteSubject {
    -subjectState
    +getState()
    +setState()
}

class ConcreteObserver {
    -observerState
    +update()
}

Subject <|-- ConcreteSubject
Observer <|-- ConcreteObserver
Subject o-- Observer
ConcreteObserver --> ConcreteSubject
@enduml

2.2 角色詳解

  1. Subject(抽象主題)

    • 知道其觀察者,可以有任意數量的觀察者
    • 提供注冊和刪除觀察者對象的接口
  2. ConcreteSubject(具體主題)

    • 存儲對觀察者有意義的狀態
    • 當狀態改變時,向各個觀察者發出通知
  3. Observer(抽象觀察者)

    • 為所有具體觀察者定義一個更新接口
  4. ConcreteObserver(具體觀察者)

    • 維護一個指向ConcreteSubject的引用
    • 存儲與主題狀態一致的狀態
    • 實現Observer的更新接口

3. Java內置觀察者實現

Java在java.util包中提供了Observable類和Observer接口的內置實現。

3.1 Observable類

public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs = new Vector<>();
    
    public synchronized void addObserver(Observer o) {
        if (o == null) throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    
    public void notifyObservers() {
        notifyObservers(null);
    }
    
    public void notifyObservers(Object arg) {
        Object[] arrLocal;
        synchronized (this) {
            if (!changed) return;
            arrLocal = obs.toArray();
            clearChanged();
        }
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    
    protected synchronized void setChanged() {
        changed = true;
    }
    
    protected synchronized void clearChanged() {
        changed = false;
    }
}

3.2 Observer接口

public interface Observer {
    void update(Observable o, Object arg);
}

3.3 使用示例

// 被觀察者
class WeatherData extends Observable {
    private float temperature;
    
    public void measurementsChanged() {
        setChanged();  // 必須調用
        notifyObservers();
    }
    
    public void setMeasurements(float temperature) {
        this.temperature = temperature;
        measurementsChanged();
    }
}

// 觀察者
class CurrentConditionsDisplay implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)o;
            // 更新顯示
        }
    }
}

// 使用
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay display = new CurrentConditionsDisplay();
weatherData.addObserver(display);
weatherData.setMeasurements(80);

4. 自定義觀察者模式實現

4.1 自定義Subject接口

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

4.2 自定義Observer接口

public interface Observer {
    void update(float temp, float humidity, float pressure);
}

4.3 具體實現

public class WeatherData implements Subject {
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;
    
    public WeatherData() {
        observers = new ArrayList<>();
    }
    
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    
    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    
    @Override
    public 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();
    }
}

5. 推模型與拉模型

5.1 推模型(Push Model)

在推模型中,Subject在通知Observer時,會將詳細的數據通過參數傳遞給Observer。

public interface PushObserver {
    void update(float temp, float humidity, float pressure);
}

public class WeatherData implements Subject {
    // ...
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }
}

5.2 拉模型(Pull Model)

在拉模型中,Subject在通知Observer時,只傳遞最少的通知,Observer根據需要從Subject中拉取數據。

public interface PullObserver {
    void update(Subject subject);
}

public class WeatherData implements Subject {
    // ...
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
    
    public float getTemperature() { return temperature; }
    public float getHumidity() { return humidity; }
    public float getPressure() { return pressure; }
}

5.3 比較

特性 推模型 拉模型
數據傳遞方式 Subject主動推送所有數據 Observer根據需要從Subject拉取數據
靈活性 較低,Subject決定傳遞哪些數據 較高,Observer決定需要哪些數據
耦合度 較高,Observer接口需包含所有參數 較低,只需Subject引用
性能 可能傳遞不必要的數據 只獲取需要的數據

6. 觀察者模式優缺點

6.1 優點

  1. 松耦合:Subject和Observer可以獨立變化,互不影響
  2. 動態關系:可以在運行時動態建立和刪除觀察關系
  3. 廣播通信:一個Subject可以通知多個Observer
  4. 符合開閉原則:新增Observer無需修改Subject代碼

6.2 缺點

  1. 通知順序不確定:Observer的更新順序通常無法保證
  2. 性能問題:大量Observer或頻繁更新可能導致性能問題
  3. 循環依賴:不當使用可能導致循環調用
  4. 內存泄漏:Observer未正確注銷可能導致內存泄漏

7. 典型應用場景

  1. GUI事件處理:如按鈕點擊事件監聽
  2. 發布-訂閱系統:如消息隊列、事件總線
  3. 模型-視圖分離:如MVC架構中的模型和視圖
  4. 監控系統:如系統狀態監控和報警
  5. 股票行情系統:股票價格變化通知投資者

8. 與其他模式的關系

8.1 與中介者模式

  • 相似點:都用于減少對象間的直接耦合
  • 區別:中介者模式通過中介對象集中控制通信,而觀察者模式通過Subject-Observer的直接通知

8.2 與責任鏈模式

  • 相似點:都涉及對象間的通知機制
  • 區別:責任鏈模式中處理器形成鏈條,觀察者模式中所有Observer平等接收通知

8.3 與發布-訂閱模式

  • 相似點:都是對象間的一對多依賴關系
  • 區別:發布-訂閱模式通常引入中間代理,解耦更徹底

9. JDK中的觀察者模式

9.1 java.util.Observable/Observer

如前所述,Java提供了內置實現,但自Java 9已被標記為@Deprecated。

9.2 JavaBeans屬性變更監聽

public class BeanExample {
    private PropertyChangeSupport support = new PropertyChangeSupport(this);
    
    public void addPropertyChangeListener(PropertyChangeListener pcl) {
        support.addPropertyChangeListener(pcl);
    }
    
    public void removePropertyChangeListener(PropertyChangeListener pcl) {
        support.removePropertyChangeListener(pcl);
    }
    
    private String value;
    
    public void setValue(String newValue) {
        String oldValue = this.value;
        this.value = newValue;
        support.firePropertyChange("value", oldValue, newValue);
    }
}

9.3 Swing中的觀察者模式

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // 處理按鈕點擊
    }
});

10. Spring框架中的應用

10.1 ApplicationEvent機制

// 自定義事件
public class CustomEvent extends ApplicationEvent {
    public CustomEvent(Object source) {
        super(source);
    }
}

// 事件發布者
@Component
public class EventPublisher {
    @Autowired
    private ApplicationEventPublisher publisher;
    
    public void publish() {
        publisher.publishEvent(new CustomEvent(this));
    }
}

// 事件監聽器
@Component
public class EventListener {
    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        // 處理事件
    }
}

10.2 @EventListener注解

Spring 4.2+提供了更靈活的事件監聽方式:

@EventListener(condition = "#event.success")
public void handleSuccessfulEvent(CustomEvent event) {
    // 條件監聽
}

11. 實際案例演示

11.1 股票價格監控系統

// Subject
public class StockMarket {
    private Map<String, Double> prices = new HashMap<>();
    private List<StockObserver> observers = new ArrayList<>();
    
    public void addObserver(StockObserver observer) {
        observers.add(observer);
    }
    
    public void setPrice(String symbol, double price) {
        prices.put(symbol, price);
        notifyObservers(symbol, price);
    }
    
    private void notifyObservers(String symbol, double price) {
        for (StockObserver observer : observers) {
            observer.update(symbol, price);
        }
    }
}

// Observer
public interface StockObserver {
    void update(String symbol, double price);
}

// Concrete Observer
public class StockTrader implements StockObserver {
    private String name;
    
    public StockTrader(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String symbol, double price) {
        System.out.printf("%s received update: %s is now %.2f\n", name, symbol, price);
    }
}

// 使用
StockMarket market = new StockMarket();
market.addObserver(new StockTrader("Trader1"));
market.addObserver(new StockTrader("Trader2"));
market.setPrice("AAPL", 150.25);

12. 常見問題與解決方案

12.1 線程安全問題

問題:多線程環境下Subject狀態變更和通知可能不同步

解決方案: - 使用同步機制保護共享狀態 - 考慮使用線程安全的集合類 - 使用CopyOnWriteArrayList存儲觀察者列表

public class ThreadSafeSubject implements Subject {
    private final List<Observer> observers = new CopyOnWriteArrayList<>();
    
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    
    @Override
    public void notifyObservers() {
        for (Observer o : observers) {
            o.update();
        }
    }
}

12.2 內存泄漏

問題:Observer未正確注銷導致無法被垃圾回收

解決方案: - 提供明確的注銷機制 - 使用WeakReference存儲觀察者 - 在適當生命周期點自動注銷

public class WeakSubject implements Subject {
    private final List<WeakReference<Observer>> observers = new ArrayList<>();
    
    @Override
    public void registerObserver(Observer o) {
        observers.add(new WeakReference<>(o));
    }
    
    @Override
    public void notifyObservers() {
        Iterator<WeakReference<Observer>> it = observers.iterator();
        while (it.hasNext()) {
            Observer o = it.next().get();
            if (o != null) {
                o.update();
            } else {
                it.remove(); // 清理已被GC的觀察者
            }
        }
    }
}

13. 觀察者模式變體

13.1 事件總線(Event Bus)

public class EventBus {
    private final Map<Class<?>, List<Consumer<?>>> handlers = new ConcurrentHashMap<>();
    
    public <T> void subscribe(Class<T> eventType, Consumer<T> handler) {
        handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
    }
    
    public <T> void publish(T event) {
        List<Consumer<?>> eventHandlers = handlers.get(event.getClass());
        if (eventHandlers != null) {
            for (Consumer<?> handler : eventHandlers) {
                ((Consumer<T>) handler).accept(event);
            }
        }
    }
}

13.2 反應式流(Reactive Streams)

public class SimplePublisher implements Publisher<String> {
    private final ExecutorService executor = Executors.newFixedThreadPool(3);
    
    @Override
    public void subscribe(Subscriber<? super String> subscriber) {
        subscriber.onSubscribe(new Subscription() {
            @Override
            public void request(long n) {
                executor.submit(() -> {
                    for (long i = 0; i < n; i++) {
                        subscriber.onNext("Event " + i);
                    }
                });
            }
            
            @Override
            public void cancel() {
                // 處理取消邏輯
            }
        });
    }
}

14. 性能考量

  1. 通知效率:大量Observer時,線性通知可能成為瓶頸

    • 解決方案:考慮使用分層觀察、異步通知
  2. 內存占用:每個Observer通常需要存儲Subject引用

    • 解決方案:使用弱引用、及時注銷
  3. 線程阻塞:同步通知可能導致調用鏈阻塞

    • 解決方案:異步通知、事件隊列
  4. 事件風暴:頻繁狀態變化導致過多通知

    • 解決方案:合并通知、節流處理

15. 總結

觀察者模式是Java中一種極其重要的設計模式,它通過定義對象間的一對多依賴關系,實現了松耦合的交互方式。從早期的AWT/Swing事件模型,到現代的Spring事件機制,觀察者模式在Java生態中有著廣泛的應用。

關鍵要點:

  1. 理解推模型和拉模型的區別及適用場景
  2. 注意線程安全和內存泄漏問題
  3. 了解Java內置實現及其局限性
  4. 掌握在Spring等現代框架中的應用方式
  5. 根據實際需求選擇合適的變體模式

最佳實踐建議:

  1. 對于簡單場景,可以使用Java內置的Observer/Observable
  2. 對于復雜系統,考慮實現自定義的觀察者模式
  3. 在多線程環境下特別注意同步問題
  4. 考慮使用事件總線等高級變體滿足復雜需求
  5. 在性能敏感場景,評估觀察者模式的開銷

觀察者模式雖然簡單,但正確使用需要仔細考慮各種邊界條件和性能影響。希望本文能幫助您全面理解并有效應用這一經典設計模式。 “`

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女