觀察者模式(Observer Pattern)
概念
觀察者模式:定義了對象之間的一對多個關係,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。
組成
圖片來源wiki
觀察者模式由 主題 和 觀察者 組成。
接口主題類(Subject):主題接口,對象使用該接口成爲觀察者,主題是有狀態的對象。
具體主題類(ConcreteSubject):該類主要實現了 notifyObserver 方法,保證在主題更新時通知所有的觀察者。
接口觀察者類(Observer):所有觀察者必須實現的一個接口,該接口一般僅有 update 方法,當主題改變時被調用。
具體觀察類(ConcreteObserver):該類主要實現了 update 方法,並維護一個指向主題類的引用。
例子:
時間主題,假設北京時間爲標準時間,每個地方都要根據北京時間來調整本地時間。那麼北京時間是主題,地方是觀察者。
時間主題接口類
package DesignPattern.Strategy.Observer;
public interface TimeSubject {
public void registerObserver(TimeObserver to);
public void removeObserver(TimeObserver to);
public void notifyObservers();
}
時間觀察者接口類
package DesignPattern.Strategy.Observer;
public interface TimeObserver {
public void update(int hour, int minute, int second);
}
展示接口(每個地方展示時間方式不同)
package DesignPattern.Strategy.Observer;
public interface Display {
public void display();
}
具體時間主題類:
package DesignPattern.Strategy.Observer;
import java.util.ArrayList;
public class TimeConcreteSubject implements TimeSubject {
private ArrayList observers;
private int hour;
private int minute;
private int second;
public TimeConcreteSubject() {
observers = new ArrayList();
}
/* 註冊觀察者 */
public void registerObserver(TimeObserver to) {
observers.add(to);
}
/* 移除觀察者 */
public void removeObserver(TimeObserver to) {
observers.remove(to);
}
/* 通知所有觀察者 */
public void notifyObservers() {
for (int i = 0; i < observers.size(); ++i) {
TimeObserver to = (TimeObserver) observers.get(i);
to.update(hour, minute, second);
}
}
public void timeChange(int hour, int minute, int second) {
this.hour = hour;
this.minute = minute;
this.second = second;
notifyObservers();
}
}
具體時間觀察者類:
package DesignPattern.Strategy.Observer;
public class TimeConcreteObserver implements TimeObserver,Display {
private int hour;
private int minute;
private int second;
private TimeSubject timeSubject;
public TimeConcreteObserver(TimeSubject timeSubject) {
this.timeSubject = timeSubject;
timeSubject.registerObserver(this);
}
/* 觀察者接受主題更新 */
public void update(int hour, int minute, int second) {
this.hour = hour;
this.minute = minute;
this.second = second;
}
public void display() {
System.out.println("時:" + hour + " 分:" + minute + " 秒:" + second);
}
}
測試:
package DesignPattern.Strategy.Observer;
public class TimeStation {
public static void main(String []args) {
TimeConcreteSubject timeConcreteSubject = new TimeConcreteSubject();
TimeConcreteObserver timeConcreteObserver = new TimeConcreteObserver(timeConcreteSubject);
timeConcreteSubject.timeChange(10, 50, 29);
timeConcreteObserver.display();
}
}
注意:
觀察者模式有兩種,上面演示的是 推,還有一種觀察者主動 拉。(主題中加入 notifyOneObserver(Observer o) 即可)
Java JDK 中內置了觀察者模式,我們可直接使用。
適用場景
- 當其中一個對象的變更會 影響 其他對象,卻又不知道 有多少 對象必須被同時變更時。
- 當對象應該有能力通知其他對象,又不應該知道其他對象的實做細節時。
- 當抽象個體有兩個互相依賴的層面時。封裝這些層面在單獨的對象內將可允許程序員單獨地去變更與重複使用這些對象,而不會產生兩者之間交互的問題。
優缺點
優點:
- 觀察者模式可以實現表示層和數據邏輯層的分離。觀察者模式通常與 MVC 範式有關係。在 MVC 中,觀察者模式被用來降低 model 與 view 的耦合程度。
- 觀察者和主題之間是松耦合的方式結合。
缺點:
- 如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
- 如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
- 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化。
設計原則:
找出程序中會變化的方面,然後將其和固定不變的方面分離。
針對接口編程,不針對實現編程。
多用組合,少用繼承。
參考:
wiki百科
Head First 設計模式