觀察者模式
- 理解:觀察者模式通過主題和觀察者的形式來通知觀察者主題即時更新的信息。主題有修改信息的功能,並且需要提供註冊和解除功能來使觀察者可以隨時訂閱來接收消息或者取消訂閱選擇不接受消息,因此主題還需要有一個通知的功能來推送消息給觀察者。對於觀察者來說都需要實現統一的Observer接口來更新主題傳送過來的信息。
定義: 觀察者模式定義了對象之家的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會手動通知並自動更新。
-
類圖展示
-
實現例子(天氣更新)
/**
* 主題接口
*/
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
/**
* 觀察者接口
*/
public interface Observer {
void update(float temp, float humidity, float pressure);
}
/**
* 具體的主題類
*/
public class WeatherData implements Subject {
private float temperature;
private float humidity;
private float pressure;
private List<Observer> observerList = Lists.newArrayList();
/**
* 註冊觀察者
*
* @param observer
*/
public void registerObserver(Observer observer) {
observerList.add(observer);
}
/**
* 移除觀察者
*
* @param observer
*/
public void removeObserver(Observer observer) {
if (observerList.indexOf(observer) >= 0) {
observerList.remove(observer);
}
}
/**
* 通知觀察者更新消息
*/
public void notifyObservers() {
for (Observer observer : observerList) {
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();
}
}
/**
* 具體觀察者
*
*/
public class TemperatureObserver implements Observer {
private float temperature;
private float humidity;
private Subject weatherData;
//初始化的時候將主題對象注入進來,並且實現註冊
public TemperatureObserver(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
/**
* 實現更新方法
*
* @param temp
* @param humidity
* @param pressure
*/
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
void display() {
System.out.println("=====temperature is====" + temperature + "=====humidity is===" + humidity);
}
}
/**
* 入口類
*/
public class AppMain {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
TemperatureObserver temperatureObserver = new TemperatureObserver(weatherData);
weatherData.setMeasurements(10L, 80, 90);
}
}
結果:=====temperature is====10.0=====humidity is===80.0
總結:觀察者模式適合一對多的場景,觀察者需要實時接收最近的消息。例如網上購票等場景。網站在發佈消息的時候,消費者就可以馬上收到信息反饋。
Java自帶的觀察者模式,大體實現和上述一樣,具體如下所示:
/**
* 主體類
*/
public class Observable {
private boolean changed = false;
// java中通過Vector來存放Observer,來確保線程安全
private Vector<Observer> obs;
public Observable() {
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);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
//java中提供changed來標識是否需要通知最新消息
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
/**
* 觀察者類
*/
public interface Observer {
//java中方法中還傳遞了arg參數來添加額外參數
void update(Observable o, Object arg);
}
總結: 編碼原則
1. 封裝變化的東西
2. 多用組合,少用繼承
3. 針對接口編程,不針對實現編程
4. 爲交互對象的鬆耦合努力