JUnit源碼中實現支持不同的使用方式:swt、swing的UI方式和控制臺方式,對于這些不同的UI如何提供統一的接口供它們獲取測試過程的信息(比如出現的異常信息,測試成功,測試失敗的代碼行數等等)?我們試想一下這個場景,當一個error或者fail產生的時候,測試能夠馬上通知這些UI客戶端:發生錯誤了,發生了什么錯誤,錯誤是什么等等。顯而易見,這是一個訂閱-發布或者源-監聽機制應用的場景,應當使用觀察者模式。那么什么是觀察者模式呢?
為了便于理解觀察者模式,舉一個現實生活中的例子:在中國好聲音比賽過程其實就是觀察者模式的最好體現,可以這樣說吳莫愁等上臺比賽者就是一個個被觀察者,而劉歡、那英、楊坤等人就是4個觀察者,被觀察者操作(唱歌)時,觀察者們就開始操作(評分),被觀察者唱歌就是通知觀察者們進行評分。
另外在第二季傳劉歡等人不在作為導師,當然其中的歌手也會變化。由上面的例子可以產出,在觀察者模式中的角色主要有兩大類四個角色即:觀察者、被觀察者兩大類,四個角色即:觀察者接口類(導師)、觀察者具體實現類(具體導師,比如劉歡等)、被觀察者接口類(歌手或者演員)、具體被觀察者(吳莫愁等人)
根據以上的例子總結一下觀察者模式的組成角色:
抽象主題角色:把所有對觀察者對象的引用保存在一個集合中,每個抽象主題角色都可以有任意數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者角色。一般用一個抽象類或接口來實現。這個映射到上面的例子就是被觀察者接口類(歌手或者演員)
抽象觀察者角色:為所有具體的觀察者定義一個接口,在得到主題的通知時更新自己。這個映射到上面的例子中的角色自然就是觀察者接口類(導師)
具體主題角色:在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個子類實現。這個映射到上面的例子中的角色自然就是被觀察者的實現類(即吳莫愁等人)
具體觀察者角色:該角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。如果需要,具體觀察者角色可以保存一個指向具體主題角色的引用。通常用一個子類實現這個映射到上面的例子中的角色自然就是觀察者具體實現類(具體導師,比如劉歡等)
再看看他們之間的關聯關系
Watcher及其子類維護一個觀察者列表,當需要通知所有的Watched對象時調用notifyWatchers方法遍歷Watched集合,并調用它們的update方法更新。而具體的觀察者實現Watched接口(或者抽象類),提供具體的更新行為。
具體實現代碼如下:
抽象主題角色:
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);
}
}
觀察者模式會達到如下效果:
1) 目標和觀察者的抽象耦合,目標僅僅與抽象層次的簡單接口Watcher松耦合,而沒有與具體的觀察者緊耦合
2) 支持廣播通信
3) 缺點是可能導致意外的更新,因為一個觀察者并不知道其他觀察者,它的更新行為也許將導致一連串不可預測的更新的行為
接著看下JUnit是怎么實現這個模式的。在junit.framework包中我們看到了一個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);
}
接著我們看下Junit中觀察者的實現類是如何實現接口類,在swt、swing的UI方式和控制臺方式這三種運行模式中都對其觀察者接口的具體實現。在這以testui運行模式調用為例。在testui模式中,TestRunner繼承BaseTestRunner而這段代碼在BaseTestRunner實現如下:
public synchronized void endTest(Test test) {
testEnded(test.toString());
}
public synchronized void addError(final Test test, final Throwable t) {
testFailed(TestRunListener.STATUS_ERROR, test, t);
}
public synchronized void addFailure(final Test test, final AssertionFailedError t) {
testFailed(TestRunListener.STATUS_FAILURE, test, t);
}
另外在testui中的ResultPrinter也實現了對TestListener的實現,具體如下:
public void addError(Test test, Throwable t) {
getWriter().print("E");
}
public void addFailure(Test test, AssertionFailedError t) {
getWriter().print("F");
原文轉自:http://blog.sina.com.cn/s/blog_6e0d94750101640x.html