JUNIT源碼探秘(五):Junit代碼分析之觀察者模式
JUnit源碼中實(shí)現(xiàn)支持不同的使用方式:swt、swing的UI方式和控制臺(tái)方式,對(duì)于這些不同的UI如何提供統(tǒng)一的接口供它們獲取測(cè)試過程的信息(比如出現(xiàn)的異常信息,測(cè)試成功,測(cè)試失敗的代碼行數(shù)等等)?我們?cè)囅胍幌逻@個(gè)場(chǎng)景,當(dāng)一個(gè)error或者fail產(chǎn)生的時(shí)候,測(cè)試能夠馬上通知這些UI客戶端:發(fā)生錯(cuò)誤了,發(fā)生了什么錯(cuò)誤,錯(cuò)誤是什么等等。顯而易見,這是一個(gè)訂閱-發(fā)布或者源-監(jiān)聽機(jī)制應(yīng)用的場(chǎng)景,應(yīng)當(dāng)使用觀察者模式。那么什么是觀察者模式呢?
為了便于理解觀察者模式,舉一個(gè)現(xiàn)實(shí)生活中的例子:在中國好聲音比賽過程其實(shí)是觀察者模式的好體現(xiàn),可以這樣說吳莫愁等上臺(tái)比賽者是一個(gè)個(gè)被觀察者,而劉歡、那英、楊坤等人是4個(gè)觀察者,被觀察者操作(唱歌)時(shí),觀察者們開始操作(評(píng)分),被觀察者唱歌是通知觀察者們進(jìn)行評(píng)分。
另外在第二季傳劉歡等人不在作為導(dǎo)師,當(dāng)然其中的歌手也會(huì)變化。由上面的例子可以產(chǎn)出,在觀察者模式中的角色主要有兩大類四個(gè)角色即:觀察者、被觀察者兩大類,四個(gè)角色即:觀察者接口類(導(dǎo)師)、觀察者具體實(shí)現(xiàn)類(具體導(dǎo)師,比如劉歡等)、被觀察者接口類(歌手或者演員)、具體被觀察者(吳莫愁等人)
根據(jù)以上的例子總結(jié)一下觀察者模式的組成角色:
抽象主題角色:把所有對(duì)觀察者對(duì)象的引用保存在一個(gè)集合中,每個(gè)抽象主題角色都可以有任意數(shù)量的觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者角色。一般用一個(gè)抽象類或接口來實(shí)現(xiàn)。這個(gè)映射到上面的例子是被觀察者接口類(歌手或者演員)
抽象觀察者角色:為所有具體的觀察者定義一個(gè)接口,在得到主題的通知時(shí)更新自己。 這個(gè)映射到上面的例子中的角色自然是觀察者接口類(導(dǎo)師)
具體主題角色:在具體主題內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)出通知。具體主題角色通常用一個(gè)子類實(shí)現(xiàn)。這個(gè)映射到上面的例子中的角色自然是被觀察者的實(shí)現(xiàn)類(即吳莫愁等人)
具體觀察者角色:該角色實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài)相協(xié)調(diào)。如果需要,具體觀察者角色可以保存一個(gè)指向具體主題角色的引用。通常用一個(gè)子類實(shí)現(xiàn)這個(gè)映射到上面的例子中的角色自然是觀察者具體實(shí)現(xiàn)類(具體導(dǎo)師,比如劉歡等)
再看看他們之間的關(guān)聯(lián)關(guān)系
Watcher及其子類維護(hù)一個(gè)觀察者列表,當(dāng)需要通知所有的Watched對(duì)象時(shí)調(diào)用notifyWatchers方法遍歷Watched集合,并調(diào)用它們的update方法更新。而具體的觀察者實(shí)現(xiàn)Watched接口(或者抽象類),提供具體的更新行為。
具體實(shí)現(xiàn)代碼如下:
抽象主題角色:
public interface Watched {
public void addWatcher(Watcher wather);
public void delWatcher(Watcher wather);
public void notifyWatchers(String str);
}
具體主題角色:
public class WatchedImpl implements Watched {
List list = newArrayList();
public void addWatcher(Watcher wather) {
list.add(wather);
}
public void delWatcher(Watcher wather) {
list.remove(wather);
}
public void notifyWatchers(String str) {
for (Watcher wather : list) {
wather.update(str);
}
}
抽象觀察者角色:
public interface Watcher {
public void update(String str);
}
具體觀察者角色:
public class WatcherImpl implements Watcher{
public void update(String str) {
System.out.print(str);
}
}
觀察者模式會(huì)達(dá)到如下效果:
1) 目標(biāo)和觀察者的抽象耦合,目標(biāo)僅僅與抽象層次的簡單接口Watcher松耦合,而沒有與具體的觀察者緊耦合
2) 支持廣播通信
3) 缺點(diǎn)是可能導(dǎo)致意外的更新,因?yàn)橐粋(gè)觀察者并不知道其他觀察者,它的更新行為也許將導(dǎo)致一連串不可預(yù)測(cè)的更新的行為
接著看下JUnit是怎么實(shí)現(xiàn)這個(gè)模式的。在junit.framework包中我們看到了一個(gè)Watcher觀察者接口——TestListener,代碼如下:
public interfaceTestListener {
public void addError(Test test, Throwable t);
public void addFailure(Test test, AssertionFailedError t);
public void endTest(Test test);
public void startTest(Test test);
}