在GOF的設計模式一書中將觀察者定義爲一種對象的行爲模式,又叫做發佈-訂閱模式(Publish/Subscribe)、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。
觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使他們能夠自動更新自己。
Java的標準庫中包含了一個Observer接口和一個Observable類。
Observer接口很簡單,只包含了一個update方法:
public interface Observer {
void update(Observable o, Object arg);
}
Observable類的大綱如下
Observable類自身維護了一個包含Observer的列表,和一個表示自身狀態的changed標誌,當自身狀態發生變化時就調用notifyObservers通知所有註冊的觀察者。notifyObservers方法通過對調用每一個成員的Observer接口的update方法來實現對觀察者的通知。
使用這種模式的例子在網上已經有很多了,這裏不再具體舉例,但需要指出的是在客戶代碼使用Java標準庫提供的Observer/Observable對來實現觀察者模式時需要自己編寫一個繼承Observable類的被觀察者和一個實現Observer接口的觀察者。這種方式有一個缺陷,因爲Java語言是單繼承的,一旦客戶代碼中的被觀察者已經繼承了一個其他的類則無法再次繼承Observable類,這也大大的限制了這種模式的廣泛使用。
在GOF一書中則描述了另一種使用觀察者的模式,即將觀察者和被觀察者全部做成抽象接口,下面是這種模式的靜態類圖。
抽象被觀察者提供註冊和刪除觀察者對象的接口,一個通知觀察者的接口。抽象觀察者在被觀察者狀態發生變化並得到通知時提供一個更新自己的接口。具體被觀察者和具體觀察者是對抽象接口的具體實現。其中具體被觀察者維護一個所有註冊的觀察者的列表,而觀察者的update方法接受一個指向被觀察者的引用。
觀察者模式所適用的一個很重要的場景是當一個對象必須通知其他對象,而它又不能假定其它對象是誰。也就是說,你不希望這些對象是緊密耦合的。這裏通過使用接口來達到解耦的目的。這種觀察者模式的應用使用接口而不是類解決了Java單繼承的問題,這種模型通過GOF一書而得到廣爲流傳,網絡上的例子很多,這裏也不具體舉例了。但是我們也看到,由於Observer接口只有一個update方法,也就說是當觀察者接受到通知它只能作出同樣的迴應。在一個事件-觸發模型中,由於觀察者(或者說事件監聽者)需要對不同的事件作出迴應,那這種模型就不再適用。就算用起來也會顯得很繁瑣,比如在具體觀察者的update方法中指定參數類型,或者使用反射,又或者傳入一個參數來表明事件的類型,但不論如何都不是一種很好的實現方式。而可以複用的只有兩個接口,每次使用的時候必須要實現兩個具體類,沒能達到很高的複用性。
下面介紹一種新的Observer模式的實現方法。這裏使用了泛型模板,通過參數化Observer接口來實現任意定製更新方法。另外還引入了一個ObserverOp的接口,將Observer Operation的操作抽象,使得觀察者與被觀察者解耦。
/** Observable接口代碼 */
public interface Observable<T> {
public static interface ObserverOp<T>
{
public void apply (T observer);
}
public void addObserver(T observer);
public void deleteObserver(T observer);
public void notifyObservers(ObserverOp<T> obop);
}
每一個被觀察者都要實現這個接口則顯得比較繁瑣,所以這裏提供了一個默認實現。
/** Observable接口的默認實現代碼 */
public class DefaultObservable<T> implements Observable<T> {
private List<T> obs;;
public DefaultObservable(){
obs = new ArrayList<T>();
}
public void addObserver(T observer) {
obs.add(observer);
}
public void deleteObserver(T observer) {
obs.remove(observer);
}
public void notifyObservers(Observable.ObserverOp<T> obop) {
Iterator<T> iterator = obs.iterator();
while (iterator.hasNext()){
obop.apply(iterator.next());
}
}
}
下面舉例說明這種觀察者模式的使用,假如我們有一個Session類,當Session的狀態發生變化時需要通知客戶代碼,並假設狀態的變化有激活和靜默兩種,這樣我們就有了一個SessionObserver接口。
/** SessionObserver接口的代碼 */
public interface SessionObserver {
public void SessionActivate(Session session);
public void SessionPassivate(Session session);
}
同時我們也有一個對應的Session類。
/** Session類的代碼 */
public class Session {
private Observable<SessionObserver> obs = new DefaultObservable<SessionObserver>();
public void addSessionObserver(SessionObserver observer){
obs.addObserver(observer);
}
public void Activate(){
//do activate
Notifier noty = new Notifier(SESSION_ACTIVATE);
obs.notifyObservers(noty);
}
public void Passivate(){
//do passivate
Notifier noty = new Notifier(SESSION_PASSIVATE);
obs.notifyObservers(noty);
}
private class Notifier implements Observable.ObserverOp<SessionObserver>{
private int code;
public Notifier(int code){
this.code=code;
}
public void apply(SessionObserver obs){
switch (code){
case SESSION_ACTIVATE:
obs.SessionActivate(Session.this);
break;
case SESSION_PASSIVATE:
obs.SessionPassivate(Session.this);
break;
}
}
}
public static final int SESSION_ACTIVATE = 1;
public static final int SESSION_PASSIVATE = 2;
}
在這裏我們內置了一個Notifier類,這個類實現了ObserverOp接口,將我們所有的Observer Operation操作都包含了進去,使得其餘部分的代碼十分簡潔。在這裏Notifier類實際上實現了設計模式中另一個strategy(策略)模式,這個stragety(策略)將相關的操作算法封裝在了一個類裏。對於不同的Observer接口可以十分方便的替換我們的算法。下面是客戶代碼,當Session被激活或者靜默時客戶代碼被自動通知並作出反應。
/** 客戶代碼 */
public class Client implements SessionObserver {
public void SessionActivate(Session session) {
System.out.println("Session has been activated...");
}
public void SessionPassivate(Session session) {
System.out.println("Session has been passivated...");
}
public static void main (String[] args){
Client client = new Client();
Session session = new Session();
session.addSessionObserver(client);
session.Activate();
session.Passivate();
}
}
通過觀察上面的例子我們可以看到,使用這種觀察者模式的模型,當被觀察者被通知時,可以根據通知的內容調用相應的方法,在這裏就是當Session被激活時調用SessionActivate方法,當Session被靜默時調用SessionPassivate方法,而不僅僅是一個簡單的update方法。但是這種模型也有一個問題,就是在這裏Session是一個被觀察者的身份,但是因爲使用了泛型的模板而無法繼承DefaultObservable類,或實現Observable接口,使得IS-A關係變成了HAS-A,邏輯上不是那麼的清晰。解決的辦法是取消Observable接口和DefaultObservable類,使用一個ObserversList來代替,在Session中直接維護一個ObserversList,那麼在邏輯上Session自然就成了一個被觀察者。
下面我們來看下新模型下的ObserversList類和修改過的Session類。
/** ObserverList的實現代碼 */
public class ObserverList<T> extends ArrayList<T> {
public static interface ObserverOp<T>
{
public void apply (T observer);
}
public void addObserver(T observer){
add(observer);
}
public void deleteObserver(T observer){
remove(observer);
}
public void notifyObservers(Observable.ObserverOp<T> obop) {
int count = size();
if (obs == null ){
obs = (T[])new Object[count];
}
toArray(obs);
for(int i=0;i<count;i++){
obop.apply(obs[i]);
}
}
private T[] obs;
}
/** 修改過的Session類的實現代碼 */
public class Session {
private ObserverList<SessionObserver> obs = new ObserverList<SessionObserver>();
public void addSessionObserver(SessionObserver observer){
obs.addObserver(observer);
}
public void Activate(){
//do activate
Notifier noty = new Notifier(SESSION_ACTIVATE);
obs.notifyObservers(noty);
}
public void Passivate(){
//do passivate
Notifier noty = new Notifier(SESSION_PASSIVATE);
obs.notifyObservers(noty);
}
private class Notifier implements Observable.ObserverOp<SessionObserver>{
private int code;
public Notifier(int code){
this.code=code;
}
public void apply(SessionObserver obs){
switch (code){
case SESSION_ACTIVATE:
obs.SessionActivate(Session.this);
break;
case SESSION_PASSIVATE:
obs.SessionPassivate(Session.this);
break;
}
}
}
public static final int SESSION_ACTIVATE = 1;
public static final int SESSION_PASSIVATE = 2;
}
在這個新的模型下,ObserverList繼承自ArrayList,而Session類裏維護了這個包含所有觀察者的列表,所以在邏輯上自然而然的成立了IS-A Observable的關係,其他代碼都維持不變。現在ObserverList類可以被放到通用的類庫中被反覆使用,而客戶則可以根據需要定製自己的Observer接口,並將相對應的算法操作封裝在一個具體的ObserverOp類中。