點擊上方「Java知音」,選擇「置頂公眾號」
技術文章第一時間送達!
作者:Turwe
www.jianshu.com/p/1025f644f100
觀察者模式定義觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。
關鍵字Observable
即被觀察者,也可以被叫做主題(Subject)是被觀察的對象。通常有註冊方法(register),取消註冊方法(remove)和通知方法(notify)。
Observer
即觀察者,可以接收到主題的更新。當對某個主題感興趣的時候需要註冊自己,在不需要接收更新時進行註銷操作。
例子與應用舉一個生活中的例子:比如用戶從報社訂閱報紙,報社和用戶之間是一對多依賴,用戶可以在報社訂閱(register)報紙,報社可以把最新的報紙發給用戶(notify),用戶自動收到更新。在用戶不需要的時候還可以取消註冊(remove)。
再比如Android中的EventBus,Rxjava的實現都是基於觀察者模式的思想。再比如回調函數:Android中對Button的點擊監聽等等。
觀察者模式可以用來解耦
自己用代碼實現一個觀察者模式現在我們用代碼來實現上面訂閱報紙的例子:
NewProvider作為對於報社的抽象,每隔兩秒鐘向用戶發送報紙;User作為用戶的抽象,可以收到報紙。NewsModel作為對報紙本身的抽象。
/**
* 被觀察者接口定義
*/
public interface MyObserverable {
void register(MyObserver myObserver);
void remove(MyObserver myObserver);
void send(NewsModel model);
}
/**
* 觀察者接口定義
*/
public interface MyObserver {
void receive(NewsModel model);
}
/**
* 對於報社的抽象,實現了被觀察者接口,每隔2s發送一次報紙
*/
public class NewsProvider implements MyObserverable {
private static final long DELAY = 2 * 1000;
private List<MyObserver> mObservers; //我們用一個List來維護所有的觀察者對象
public NewsProvider() {
mObservers = new ArrayList<>();
generateNews();
}
/**
* 模擬產生新聞,每個2s發送一次
*/
private void generateNews() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
int titleCount = 1;
int contentCount = 1;
@Override
public void run() {
send(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
}
}, DELAY, 1000);
}
@Override
public void register(MyObserver myObserver) {
if (myObserver == null)
return;
synchronized (this) {
if (!mObservers.contains(myObserver))
mObservers.add(myObserver);
}
}
@Override
public synchronized void remove(MyObserver myObserver) {
mObservers.remove(myObserver);
}
@Override
public void send(NewsModel model) {
for (MyObserver observer : mObservers) {
observer.receiveNews(model);
}
}
}
/**
* 對於用戶的抽象
*/
public class User implements MyObserver {
private String mName;
public User(String name) {
mName = name;
}
@Override
public void receive(NewsModel model) {
System.out.println(mName + " receive news:" + model.getTitle() + " " + model.getContent());
}
}
/**
* 測試類
*/
public class Test {
public static void main(String[] args) {
NewsProvider provider = new NewsProvider();
User user;
for (int i = 0; i < 10; i++) {
user = new User("user:"+i);
provider.register(user);
}
}
}
運行結果如下:
這樣我們就自己動手完成了一個簡單的觀察者模式。
其實在JDK的util包內Java為我們提供了一套觀察者模式的實現,在使用的時候我們只需要繼承Observable和Observer類即可,其實觀察者模式十分簡單,推薦閱讀JDK的實現代碼真心沒有幾行。此外在JDK的實現中還增加了一個布爾類型的changed域,通過設置這個變量來確定是否通知觀察者。
下面我們用JDK的類來實現一遍我們的觀察者模式:
public class NewsProvider extends Observable {
private static final long DELAY = 2 * 1000;
public NewsProvider() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
private int titleCount = 1;
private int contentCount = 1;
@Override
public void run() {
setChanged(); //調用setChagned方法,將changed域設置為true,這樣才能通知到觀察者們
notifyObservers(new NewsModel("title:" + titleCount++, "content:" + contentCount++));
}
}, DELAY, 1000);
}
}
public class User implements Observer {
private String mName;
public User(String name) {
mName = name;
}
@Override
public void update(Observable observable, Object data) {
NewsModel model = (NewsModel) data;
System.out.println(mName + " receive news:" + model.getTitle() + " " + model.getContent());
}
}
非常簡單有木有
回調函數與觀察者模式
關於回調函數的定義在知乎上看到過一個很贊的解釋:
你到一個商店買東西,剛好你要的東西沒有貨,於是你在店員那裡留下了你的電話,過了幾天店裡有貨了,店員就打了你的電話,然後你接到電話後就到店裡去取了貨。在這個例子裡,你的電話號碼就叫回調函數,你把電話留給店員就叫登記回調函數,店裡後來有貨了叫做觸發了回調關聯的事件,店員給你打電話叫做調用回調函數,你到店裡去取貨叫做響應回調事件。回答完畢。
在Android中我們有一個常用的回調:對與View點擊事件的監聽。現在我們就來分析一下對於View的監聽。
通常在我們使用的時候是這樣的:
xxxView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
});
這樣我們就註冊好了一個回調函數。
我們可以在View的源碼裡發現這個接口:
/**
* Interface definition for a callback to be invoked when a view is clicked.
*/
public interface OnClickListener {
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
void onClick(View v);
}
當你setClickListener的時候在View的源碼中可以看到對本地OnClickListener的初始化
/**
* Register a callback to be invoked when this view is clicked. If this view is not
* clickable, it becomes clickable.
*
* @param l The callback that will run
*
* @see #setClickable(boolean)
*/
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
當你的點擊到一個View後Android系統經過一系列的調用最後到了View的performClick方法中:
/**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
* a sound, etc.
*
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
就在這裡,觸發了你的onClick方法,然後執行方法體。
這裡我們的被觀察者就是View,他的註冊方法(register)就是setOnClickListener(),通知方法就是performClick;而OnClickListener就是觀察者。只不過這裡的只能註冊一個觀察對象而已。
參考https://www.zhihu.com/question/19801131/answer/13005983#showWechatShareTip
END
Java面試題專欄
我知道你 「在看」