觀察者模式是一種經常使用的設計模式,在軟體系統中物件并不是孤立存在的,一個物件行為的改變可能會導致其他與之存在依賴關系的物件行為發生改變,觀察者模式用于描述物件之間的依賴關系,
模式動機
很多情況下,物件不是孤立存在的,想象這么一個場景:你和女朋友去旅行,晚上回到賓館,女朋友穿著厚厚的大衣,從外表看上去就是個臃腫的包子,你沒有反應,等到女朋友洗完澡裹著浴巾出來以后,你立馬眼睛都瞪直了,活脫脫一個大色狼(不...不要盯著人家看了啦)
從例子中,我們不難分離出兩類角色,一類為觀察者,如色瞇瞇的你,另一類就是被觀察者所觀察的目標,如性感的女朋友,如果觀察目標發生某個動作,觀察者就會有回應,
建立一種物件與物件之間的依賴關系,一個物件發生改變時會自動通知其他物件,其他物件將相應做出反應,發生改變的物件稱為觀察目標,被通知的物件稱為觀察者,一個觀察目標可以對應多個觀察者,而且觀察者之間沒有相互聯系,可以根據需要增加和洗掉觀察者,這就是觀察者模式的模式動機,
模式定義
定義物件間的一種一對多依賴關系,使得每當一個物件狀態發生改變時,其相關依賴物件皆得到通知并自動更新,觀察者模式又叫發布-訂閱模式(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)或從屬者模式(Dependents)模式,觀察者模式是一種物件行為型模式,
模式分析
觀察者模式描述了如何建立物件與物件之間的依賴關系,那么具體如何構造呢?
這一模式的關鍵物件是觀察目標和觀察者,一個目標可以有任意多個與之相依賴的觀察者,一旦目標狀態發生變化,所有觀察者都將得到通知,
作為對這個通知的回應,每個觀察者都將即時更新自己的狀態,與目標狀態同步,這種互動也稱發布-訂閱(publish-subscribe),
目標是通知的發布者,它發出通知時不需要知道誰是它的觀察者,可以有任意數目的觀察訂閱并接收通知,
由此看一下根據該模式得出的類圖

抽象目標 Subject,典型代碼如下
import java.util.*;
public abstract class Subject {
// 定義一個集合存盤任意數量的觀察者物件
protected ArrayList<Observer> observers = new ArrayList<Observer>();
// 增加一個觀察者
public abstract void attach(Observer observer);
// 洗掉一個觀察者
public abstract void detach(Observer observer);
// 通知各個觀察者并呼叫它們的 update() 方法
public abstract void notifyObservers();
}
具體目標類 ConcreteSubject 是實作 Subject 的一個具體子類,典型代碼如下
public class ConcreteSubject extends Subject {
// 向集合中添加一個觀察者
public void attach(Observer observer) {
observers.add(observer);
}
// 從集合中洗掉一個觀察者
public void detach(Observer observer) {
observers.remove(observer);
}
// 回圈呼叫集合中觀察者的 updates() 方法
public void notifyObservers() {
for (Observer obs : observers) {
obs.update();
}
}
}
也可以把 attach() 和 detach() 方法的實作放在 Subject 中,這樣就無需每個子類都去實作一次了(如果沒有特別需求的話),另外,它還可以實作在目標類中定義的抽象業務邏輯方法(如果有的話)
抽象觀察者一般定義為一個介面,其中宣告 update() 方法,這個方法在其子類中實作,不同的觀察者具有不同的更新回應方法,典型代碼如下
public interface Observer {
public void update();
}
具體觀察者 ConcreteObserver 中實作 update() 方法,其典型代碼如下
public class ConcreteObserver implements Observer {
public void update() {
// 具體更新代碼
}
}
在使用時,客戶端首先創建具體目標物件以及具體觀察者物件(也可以使用組態檔),然后,呼叫目標物件的 attach() 方法,將這個觀察者物件在目標物件中登記,也就是將它加入到目標物件的觀察者集合中
Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.attach(observer);
subject.notifyObservers();
客戶端呼叫目標物件的 notifyObservers() 方法時,將呼叫在其觀察者集合中注冊的觀察者物件的 update() 方法,實作狀態的更新
當然,在有些復雜的情況下,具體觀察者類 ConcreteObserver 的 update() 方法在執行時,需要使用到 ConcreteSubject 中的狀態(屬性)
舉個例子,我們經常會用可視化的圖表(如柱狀圖、餅狀圖)來顯示資料,同樣的資料可能有不同的圖表顯示方法(即不同的具體觀察者),如果資料發生改變,圖示也應該實時做出改變,那自然的,圖表在更新時肯定需要用到新的資料吧
如果像上述而言,那么 ConcreteObserver 和 ConcreteSubject 之間還存在關聯關系,在 ConcreteObserver 中定義一個 ConcreteSubject 實體,通過該實體獲取存盤在 ConcreteSubject 中的狀態屬性,但這樣一來,系統的擴展性將受到一定影響,增加新的具體目標類有時候需要修改原有觀察者的代碼,在一定程度上違反了開閉原則,但如果原有觀察者無須關聯新增具體目標,則系統擴展性不受影響
// 具體目標類
public class ConcreteSubject extends Subject {
// 方便起見,attahch() 等抽象方法在抽象層做了實作了
// 具體目標類狀態
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
// 具體觀察者類
public class ConcreteObserver implements Observer {
private Subject subject = new ConcreteSubject();
private int observerState;
public void update() {
observerState = subject.getState();
// 具體更新代碼
}
}
模式優缺點
觀察者模式的優點
- 在觀察目標和觀察者之間建立一個抽象的耦合,觀察目標不需要了解其具體觀察者,只需知道它們都有一個共同的介面即可
- 觀察者模式支持廣播通信,觀察目標會向所有注冊的觀察者發出通知,簡化一對多系統設計的難度
- 觀察者模式符合開閉原則,增加新的觀察者無須修改原有系統代碼,在具體觀察者和觀察目標之間不存在關聯關系的情況下,增加新的目標也很方便
觀察者模式的缺點
- 如果一個觀察目標有很多直接和間接的觀察者,將所有觀察者都通知到會花費很多時間
- 如果在觀察者和觀察目標之間有回圈依賴的話,觀察目標會觸發它們之間的回圈呼叫,可能導致系統崩潰
- 觀察者模式沒有相應機制讓觀察者知道所觀察目標是怎么發生變化的,而僅僅只是知道觀察目標發生了變化
Java 對觀察者模式的支持
Java 提供了 Observable 類以及 Observer 介面,構成對 Java 語言對觀察者模式的支持

java.util.Observer 介面只定義一個方法,充當抽象觀察者
public interface Observer {
void update(Observable o, Object arg);
}
當觀察目標狀態發生變化時,該方法將被呼叫,在 Observer 的實作子類實作該 update() 方法,即具體觀察者可以根據需要有不同的更新行為,當呼叫觀察目標類 Observable 的 notifyObservers() 方法時,將呼叫觀察者類中的 update() 方法,
java.util.Observable 類充當觀察目標類,定義了一個向量 Vector 來存盤觀察者物件,標記變數 changed 表示觀察目標是否發生變化,true 表示發生變化,false 則表示物件不再發生改變還已經通知了所有觀察者物件,并呼叫了它們的 update() 方法,
我們可以直接使用 Observer 介面和 Observable 類來作為觀察者模式的抽象層,自定義具體的觀察者類和觀察目標類,更加方便地在 Java 語言中使用觀察者模式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/6004.html
標籤:設計模式
下一篇:設計模式之單例模式
