# 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
Subject(抽象主題)
ConcreteSubject(具體主題)
Observer(抽象觀察者)
ConcreteObserver(具體觀察者)
Java在java.util包中提供了Observable類和Observer接口的內置實現。
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;
}
}
public interface Observer {
void update(Observable o, Object arg);
}
// 被觀察者
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);
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
public interface Observer {
void update(float temp, float humidity, float pressure);
}
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();
}
}
在推模型中,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);
}
}
}
在拉模型中,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; }
}
特性 | 推模型 | 拉模型 |
---|---|---|
數據傳遞方式 | Subject主動推送所有數據 | Observer根據需要從Subject拉取數據 |
靈活性 | 較低,Subject決定傳遞哪些數據 | 較高,Observer決定需要哪些數據 |
耦合度 | 較高,Observer接口需包含所有參數 | 較低,只需Subject引用 |
性能 | 可能傳遞不必要的數據 | 只獲取需要的數據 |
如前所述,Java提供了內置實現,但自Java 9已被標記為@Deprecated。
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);
}
}
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 處理按鈕點擊
}
});
// 自定義事件
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) {
// 處理事件
}
}
Spring 4.2+提供了更靈活的事件監聽方式:
@EventListener(condition = "#event.success")
public void handleSuccessfulEvent(CustomEvent event) {
// 條件監聽
}
// 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);
問題:多線程環境下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();
}
}
}
問題: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的觀察者
}
}
}
}
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);
}
}
}
}
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() {
// 處理取消邏輯
}
});
}
}
通知效率:大量Observer時,線性通知可能成為瓶頸
內存占用:每個Observer通常需要存儲Subject引用
線程阻塞:同步通知可能導致調用鏈阻塞
事件風暴:頻繁狀態變化導致過多通知
觀察者模式是Java中一種極其重要的設計模式,它通過定義對象間的一對多依賴關系,實現了松耦合的交互方式。從早期的AWT/Swing事件模型,到現代的Spring事件機制,觀察者模式在Java生態中有著廣泛的應用。
觀察者模式雖然簡單,但正確使用需要仔細考慮各種邊界條件和性能影響。希望本文能幫助您全面理解并有效應用這一經典設計模式。 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。