單丹丹 韓冬
摘要:該文介紹了設(shè)計(jì)模式中觀察者模式的基本概念,在理解觀察者模式的基礎(chǔ)上,對(duì)Android源碼中觀察者模式的運(yùn)用進(jìn)行了深入的分析,說(shuō)明了觀察者模式在程序設(shè)計(jì)中的重要性。
關(guān)鍵詞:設(shè)計(jì)模式;觀察者模式;Android源碼
中圖分類(lèi)號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2017)02-0068-02
1觀察者模式介紹
觀察者模式定義了一種一對(duì)多的依賴(lài)關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽(tīng)某一個(gè)主題對(duì)象。這個(gè)主題對(duì)象在狀態(tài)發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,使它們能夠自動(dòng)更新自己。
2觀察者模式使用場(chǎng)景
1)當(dāng)一個(gè)抽象模型有兩個(gè)方面, 其中一個(gè)方面依賴(lài)于另一方面。將這二者封裝在獨(dú)立的對(duì)象中以使它們可以各自獨(dú)立地改變和復(fù)用。
2)當(dāng)對(duì)一個(gè)對(duì)象的改變需要同時(shí)改變其他對(duì)象, 而不知道具體有多少對(duì)象需要被改變。
3)當(dāng)一個(gè)對(duì)象必須通知其他對(duì)象,而它又不能假定其他對(duì)象是誰(shuí)。換言之, 不希望這些對(duì)象是緊密耦合的。
3觀察者模式中的角色及UML類(lèi)圖表示
1)抽象主題(Subject):它把所有觀察者對(duì)象的引用保存到一個(gè)聚集里,每個(gè)主題都可以有任何數(shù)量的觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者對(duì)象。
2)具體主題(ConcreteSubject):將有關(guān)狀態(tài)存入具體觀察者對(duì)象;在具體主題內(nèi)部狀態(tài)改變時(shí),給所有登記過(guò)的觀察者發(fā)出通知。
3)抽象觀察者(Observer):為所有的具體觀察者定義一個(gè)接口,在得到主題通知時(shí)更新自己。
4)具體觀察者(ConcreteObserver):實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題狀態(tài)協(xié)調(diào)。
用UML類(lèi)圖表示如下:
4 觀察者模式的Android源碼實(shí)現(xiàn)
Adapter是ListView中最重要的一個(gè)點(diǎn)。向ListView中添加數(shù)據(jù)后,都會(huì)調(diào)用notifyDataSetChanged()方法。notifyDataSetChanged方法定義在BaseAdapter中,代碼如下:
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
// 創(chuàng)建數(shù)據(jù)集觀察者
private final DataSetObservable mDataSetObservable = new DataSetObservable();
// 注冊(cè)觀察者
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer); }
//刪除觀察者
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer); }
//當(dāng)數(shù)據(jù)集用變化時(shí)通知所有觀察者
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();}}
上段代碼中mDataSetObservable.notifyChanged()函數(shù)代碼如下:
public class DataSetObservable extends Observable
public void notifyChanged() {
synchronized(mObservers) {
// 遍歷調(diào)用所有觀察者的onChanged方式
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();}}}
// 代碼省略
}
上段代碼中的觀察者由ListView通過(guò)setAdapter方法來(lái)設(shè)置Adapter時(shí)構(gòu)建的AdapterDataSetObserver創(chuàng)建,代碼如下:
public void setAdapter(ListAdapter adapter) {
// 若已有了一個(gè)adapter,則先注銷(xiāo)該Adapter對(duì)應(yīng)的觀察者
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver); }
// 代碼省略
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
// 獲取數(shù)據(jù)的數(shù)量
mItemCount = mAdapter.getCount();
// 創(chuàng)建一個(gè)一個(gè)數(shù)據(jù)集觀察者
mDataSetObserver = new AdapterDataSetObserver();
// 將這個(gè)觀察者注冊(cè)到Adapter中,實(shí)際上是注冊(cè)到DataSetObservable中
mAdapter.registerDataSetObserver(mDataSetObserver);
// 代碼省略
} else {// 代碼省略 }
requestLayout();}
通過(guò)以上的代碼,被觀察者、觀察者都已經(jīng)被創(chuàng)建。
下面具體說(shuō)明AdapterDataSetObserver是如何運(yùn)作。AdapterDataSetObserver定義在ListView的父類(lèi)AbsListView中,代碼如下 :
class AdapterDataSetObserver extends AdapterView
public void onChanged() {
super.onChanged();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();}}
//代碼省略
}
同時(shí)AdapterDataSetObserver又繼承自AbsListView的父類(lèi)AdapterView的AdapterDataSetObserver。當(dāng)ListView的數(shù)據(jù)發(fā)生變化時(shí),會(huì)調(diào)用Adapter的notifyDataSetChanged函數(shù),這個(gè)函數(shù)中會(huì)調(diào)用DataSetObservable的notifyChanged函數(shù),notifyChanged函數(shù)又會(huì)調(diào)用所有觀察者的onChanged方法。這就是一個(gè)觀察者模式!
最后我們對(duì)觀察者模式的源碼實(shí)現(xiàn)進(jìn)行一個(gè)梳理:AdapterView中有一個(gè)內(nèi)部類(lèi)AdapterDataSetObserver,在ListView設(shè)置Adapter時(shí)會(huì)構(gòu)建一個(gè)AdapterDataSetObserver,并且注冊(cè)到Adapter中,這就是一個(gè)觀察者。而Adapter中包含一個(gè)可觀察者數(shù)據(jù)集DataSetObservable,在數(shù)據(jù)數(shù)量發(fā)生變更時(shí),開(kāi)發(fā)者手動(dòng)調(diào)用AdapternotifyDataSetChanged,而notifyDataSetChanged實(shí)際上會(huì)調(diào)用DataSetObservable的notifyChanged函數(shù),該函數(shù)又會(huì)遍歷所有觀察者的onChanged函數(shù)。在AdapterDataSetObserver的onChanged函數(shù)中會(huì)獲取Adapter中數(shù)據(jù)集的新數(shù)量,然后調(diào)用ListView的requestLayout()方法重新進(jìn)行布局,從而對(duì)用戶(hù)界面進(jìn)行更新。
上述代碼可以運(yùn)用于雜志訂閱的事例中,雜志是主題,觀察者是訂閱者,當(dāng)出版新雜志時(shí)候,這個(gè)事件會(huì)自動(dòng)通知所有的訂閱者。除此以外也可以運(yùn)用于氣象站,為氣象站提供用戶(hù)訂閱氣象信息的服務(wù)??偸牵^察者模式在日常生活中的運(yùn)用十分廣泛。
5觀察者模式總結(jié)
觀察者模式的主要的作用就是對(duì)對(duì)象解耦,將觀察者和被觀察者完全隔離。
1)觀察者模式的優(yōu)點(diǎn)
觀察者模式解除了主題和具體觀察者的耦合,讓耦合的雙方都依賴(lài)于抽象,而不是依賴(lài)具體。
2)觀察者模式的缺點(diǎn)
在應(yīng)用觀察者模式時(shí)需要考慮一下開(kāi)發(fā)小路問(wèn)題,程序中包括一個(gè)被觀察者和多個(gè)被觀察者額,開(kāi)發(fā)和調(diào)試比較復(fù)雜,而且Java中的消息的通知默認(rèn)是順序執(zhí)行的,一個(gè)觀察者的卡頓會(huì)影響整體的執(zhí)行效率。在這種情況下,一般考慮采用異步的方式。
3)觀察者模式的重要性
觀察者將自己注冊(cè)到被觀察者的容器中時(shí),被觀察者不應(yīng)該過(guò)問(wèn)觀察者的具體類(lèi)型,而是應(yīng)該使用觀察者的接口。一個(gè)被觀察者可以對(duì)應(yīng)多個(gè)觀察者,當(dāng)被觀察者發(fā)生變化的時(shí)候,它可以將消息一一通知給所有的觀察者。觀察者模式基于接口而不是具體的實(shí)現(xiàn)的特點(diǎn),為程序設(shè)計(jì)提供了更大的靈活性,在程序設(shè)計(jì)中有著重要的地位。
參考文獻(xiàn):
[1] 何紅輝,關(guān)愛(ài)民.Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)[M].北京:人民郵電出版社, 2015:224-245.
[2] 何宏輝.Android開(kāi)發(fā)進(jìn)階從小工到專(zhuān)家[M].北京:人民郵電出版社, 2016:195-215.
[3] 郭霖.第一行代碼[M].北京:人民郵電出版社, 2014:127-135.
[4] 李剛.瘋狂安卓講義[M].北京:電子工業(yè)出版社, 2015:88-97.