軟件設計模式學習(二十三)觀察者模式


觀察者模式是一種常用的設計模式,在軟件系統中對象並非孤立存在的,一個對象行爲的改變可能會致使其餘與之存在依賴關係的對象行爲發生改變,觀察者模式用於描述對象之間的依賴關係。java


模式動機

不少狀況下,對象不是孤立存在的,想象這麼一個場景:你和女友去旅行,晚上回到賓館,女友穿着厚厚的大衣,從外表看上去就是個臃腫的包子。你沒有反應,等到女友洗完澡裹着浴巾出來之後,你立馬眼睛都瞪直了,活脫脫一個大色狼(不...不要盯着人家看了啦)設計模式

從例子中,咱們不難分離出兩類角色,一類爲觀察者,如色眯眯的你,另外一類就是被觀察者所觀察的目標,如性感的女友。若是觀察目標發生某個動做,觀察者就會有響應。this

創建一種對象與對象之間的依賴關係,一個對象發生改變時會自動通知其餘對象,其餘對象將相應作出反應。發生改變的對象稱爲觀察目標,被通知的對象稱爲觀察者,一個觀察目標能夠對應多個觀察者,並且觀察者之間沒有相互聯繫,能夠根據須要增長和刪除觀察者,這就是觀察者模式的模式動機。設計


模式定義

定義對象間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆獲得通知並自動更新。觀察者模式又叫發佈-訂閱模式(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)或從屬者模式(Dependents)模式。觀察者模式是一種對象行爲型模式。code


模式分析

觀察者模式描述瞭如何創建對象與對象之間的依賴關係,那麼具體如何構造呢?server

這一模式的關鍵對象是觀察目標和觀察者,一個目標能夠有任意多個與之相依賴的觀察者,一旦目標狀態發生變化,全部觀察者都將獲得通知。對象

做爲對這個通知的響應,每一個觀察者都將即時更新本身的狀態,與目標狀態同步,這種交互也稱發佈-訂閱(publish-subscribe)。blog

目標是通知的發佈者,它發出通知時不須要知道誰是它的觀察者,能夠有任意數目的觀察訂閱並接收通知。接口

由此看一下根據該模式得出的類圖rem

抽象目標 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 語言中使用觀察者模式。

相關文章
相關標籤/搜索