觀察者模式定義了一種一對多的依賴關(guān)系笆豁,讓多個觀察者對象同時監(jiān)聽某一個主題對象徽诲。這個主題對象在狀態(tài)上發(fā)生變化時,會通知所有觀察者對象牡属,使它們能夠自動更新自己票堵。
所謂觀察者模式
一個軟件系統(tǒng)常常要求在某一個對象的信息發(fā)生變化的時候,某些其他的對象做出相應(yīng)的改變逮栅。做到這一點(diǎn)的設(shè)計(jì)方案有很多悴势,但是為了使系統(tǒng)能夠易于復(fù)用,應(yīng)該選擇低耦合度的設(shè)計(jì)方案措伐。減少對象之間的耦合有利于系統(tǒng)的復(fù)用特纤,但是同時設(shè)計(jì)師需要使這些低耦合度的對象之間能夠維持行動的協(xié)調(diào)一致,保證高度的協(xié)作侥加。觀察者模式是滿足這一要求的各種設(shè)計(jì)方案中最重要的一種捧存。
下面看看觀察者模式的結(jié)構(gòu):
- 抽象主題(Subject)角色:抽象主題角色把所有對觀察者對象的引用保存在一個聚集(比如ArrayList對象)里,每個主題都可以有任何數(shù)量的觀察者担败。抽象主題提供一個接口矗蕊,可以增加和刪除觀察者對象,抽象主題角色又叫做抽象被觀察者(Observable)角色氢架。
- 具體主題(ConcreteSubject)角色:將有關(guān)狀態(tài)存入具體觀察者對象;在具體主題的內(nèi)部狀態(tài)改變時朋魔,給所有登記過的觀察者發(fā)出通知岖研。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。
- 抽象觀察者(Observer)角色:為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己孙援,這個接口叫做更新接口害淤。
-
具體觀察者(ConcreteObserver)角色: ** 存儲與主題的狀態(tài)自恰的狀態(tài)。具體觀察者角色實(shí)現(xiàn)抽象觀察者角色所要求的更新接口拓售,以便使本身的狀態(tài)與主題的狀態(tài) 像協(xié)調(diào)窥摄。如果需要,具體觀察者角色可以保持一個指向具體主題對象的引用础淤。
在觀察者模式中又分為推模型和拉模型**: -
推模型
主題對象向觀察者推送主題的詳細(xì)信息崭放,不管觀察者是否需要,推送的信息通常是主題對象的全部或部分?jǐn)?shù)據(jù)鸽凶。 -
拉模型
主題對象在通知觀察者的時候币砂,只傳遞少量信息。如果觀察者需要更具體的信息玻侥,由觀察者主動到主題對象中獲取决摧,相當(dāng)于是觀察者從主題對象中拉數(shù)據(jù)。一般這種模型的實(shí)現(xiàn)中凑兰,會把主題對象自身通過update()方法傳遞給觀察者掌桩,這樣在觀察者需要獲取數(shù)據(jù)的時候,就可以通過這個引用來獲取了姑食。
可能單單這樣說概念會覺得模糊波岛,還是有代碼說明吧。
推模型源碼
抽象subject類:
public abstract class Subject {
/**
* 用來保存注冊的觀察者對象
*/
private List<Observer> list = new ArrayList<Observer>();
/**
* 注冊觀察者對象
* @param observer 觀察者對象
*/
public void attach(Observer observer){
list.add(observer);
System.out.println("Attached an observer");
}
/**
* 刪除觀察者對象
* @param observer 觀察者對象
*/
public void detach(Observer observer){
list.remove(observer);
}
/**
* 通知所有注冊的觀察者對象
*/
public void nodifyObservers(String newState){
for(Observer observer : list){
observer.update(newState);
}
}
}
具體的subject類:
public class ConcreteSubject extends Subject {
private String state;
public String getState() {
return state;
}
public void change(String newState){
state = newState;
System.out.println("修改主題狀態(tài)為:" + state);
//狀態(tài)發(fā)生改變矢门,通知各個觀察者
this.nodifyObservers(state);
}
}
抽象觀察者類:
public interface Observer {
/**
* 更新接口
* @param state 更新的狀態(tài)
*/
public void update(String state);
}
具體的觀察者:
public class ConcreteObserver implements Observer {
//觀察者的狀態(tài)
private String observerState;
@Override
public void update(String state) {
/**
* 更新觀察者的狀態(tài)盆色,使其與目標(biāo)的狀態(tài)保持一致
*/
observerState = state;
System.out.println("狀態(tài)為:"+observerState);
}
}
測試類:
public class Client {
public static void main(String[] args) {
//創(chuàng)建主題對象
ConcreteSubject subject = new ConcreteSubject();
//創(chuàng)建觀察者對象
Observer observer = new ConcreteObserver();
//將觀察者對象登記到主題對象上
subject.attach(observer);
//改變主題對象的狀態(tài)
subject.change("new state");
}
}
結(jié)果:
Attached an observer
修改主題狀態(tài)為:new state
觀察者的狀態(tài)為:new state
拉模型源碼
與推模型不同的地方在與:拉模型通常都是把主題對象當(dāng)作參數(shù)傳遞。
public interface Observer {
/**
* 更新接口
* @param subject 傳入主題對象祟剔,方面獲取相應(yīng)的主題對象的狀態(tài)
*/
public void update(Subject subject);
}
在具體觀察者實(shí)現(xiàn)的地方:
@Override
public void update(Subject subject) {
/**
* 更新觀察者的狀態(tài)隔躲,使其與目標(biāo)的狀態(tài)保持一致
*/
observerState = ((ConcreteSubject)subject).getState();
System.out.println("觀察者狀態(tài)為:"+observerState);
}
subject的 nodifyObservers(String newState)變成:
public void nodifyObservers(){
for(Observer observer : list){
observer.update(this);
}
}
兩種模式的比較
- 推模型是假定主題對象知道觀察者需要的數(shù)據(jù);而拉模型是主題對象不知道觀察者具體需要什么數(shù)據(jù)物延,沒有辦法的情況下宣旱,干脆把自身傳遞給觀察者,讓觀察者自己去按需要取值叛薯。
- 推模型可能會使得觀察者對象難以復(fù)用浑吟,因?yàn)橛^察者的update()方法是按需要定義的參數(shù),可能無法兼顧沒有考慮到的使用情況耗溜。這就意味著出現(xiàn)新情況的時候组力,就可能提供新的update()方法,或者是干脆重新實(shí)現(xiàn)觀察者抖拴;而拉模型就不會造成這樣的情況燎字,因?yàn)槔P拖滦冉罚瑄pdate()方法的參數(shù)是主題對象本身,這基本上是主題對象能傳遞的最大數(shù)據(jù)集合了候衍,基本上可以適應(yīng)各種情況的需要笼蛛。
android的使用觀察者模式
其中我們可能在日常開發(fā)中每天都在用的就是adapter.notifyDataSetChanged()
BaseAdapter中:
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
其中DataSetObservable是android.database.Observable<DataSetObserver>的子類
DataSetObservable extends Observable<DataSetObserver>
mDataSetObservable.notifyChanged();的源碼:
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
具體要實(shí)現(xiàn)怎么樣的邏輯可繼承DataSetObserver對應(yīng)的方法。
當(dāng)然還有更常用的View.setOnClickListener( )的事件注冊蛉鹿。用到觀察者模型的地方很多滨砍。
其次再說說的就是就是我們或多或少有過接觸的三方框架EventBus和RxJava,它們都是基于觀察者模式實(shí)現(xiàn)的。
RxJava要比EventBus的應(yīng)用更廣泛妖异,EventBus僅僅是作為一種消息的傳遞工具惋戏,但是RxJava里面幾乎可以做任何事情,只是對于簡單的業(yè)務(wù)來說可能有些冗余随闺,EventBus相對來說更加輕量日川,EventBus有個缺點(diǎn)就是凡是使用了EventBus的類都不能進(jìn)行混淆了,否則Evnetbus就找不到OnEvent方法了矩乐。