<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    JUnit源碼分析(二)——觀察者模式

    Posted on 2007-04-05 17:19 dennis 閱讀(2907) 評論(0)  編輯  收藏 所屬分類: 模式與架構源碼解讀

        我們知道JUnit支持不同的使用方式:swt、swing的UI方式,甚至控制臺方式,那么對于這些不同的UI我們如何提供統一的接口供它們獲取測試過程的信息(比如出現的異常信息,測試成功,測試失敗的代碼行數等等)?我們試想一下這個場景,當一個error或者exception產生的時候,測試能夠馬上通知這些UI客戶端:發生錯誤了,發生了什么錯誤,錯誤是什么等等。顯而易見,這是一個訂閱-發布機制應用的場景,應當使用觀察者模式。那么什么是觀察者模式呢?

    觀察者模式(Observer)

    Observer是對象行為型模式之一

    1.意圖:定義對象間的一種一對多的依賴關系,當一個對象的狀態發現改變時,所有依賴于它的對象都得到通知并被自動更新

    2.適用場景:
    1)當一個抽象模型有兩個方面,其中一個方面依賴于另一個方面,通過觀察者模式將這兩者封裝在不同的獨立對象當中,以使它們可以獨立的變化和復用
    2)當一個對象改變時,需要同時改變其他對象,并且不知道其他對象的具體數目
    3)當一個對象需要引用其他對象,但是你又不想讓這個對象與其他對象產生緊耦合的時候

    3.UML圖:
                     
       Subject及其子類維護一個觀察者列表,當需要通知所有的Observer對象時調用Nitify方法遍歷Observer集合,并調用它們的update方法更新。而具體的觀察者實現Observer接口(或者抽象類),提供具體的更新行為。其實看這張圖,與Bridge有幾分相似,當然兩者的意圖和適用場景不同。

    4.效果:
    1)目標和觀察者的抽象耦合,目標僅僅與抽象層次的簡單接口Observer松耦合,而沒有與具體的觀察者緊耦合
    2)支持廣播通信
    3)缺點是可能導致意外的更新,因為一個觀察者并不知道其他觀察者,它的更新行為也許將導致一連串不可預測的更新的行為

    5.對于觀察者實現需要注意的幾個問題:
    1)誰來觸發更新?最好是由Subject通知觀察者更新,而不是客戶,因為客戶可能忘記調用Notify
    2)可以通過顯式傳參來指定感興趣的更新
    3)在發出通知前,確保Subject對象狀態的一致性,也就是Notify操作應該在最后被調用
    4)當Subject和Observer的依賴關系比較復雜的時候,可以通過一個更新管理器來管理它們之間的關系,這是與中介者模式的結合應用。


        討論完觀察者模式,那我們來看JUnit是怎么實現這個模式的。在junit.framework包中我們看到了一個Observer接口——TestListener,看看它的代碼:
    package junit.framework;

    /**
     * A Listener for test progress
     
    */
    public interface TestListener {
       
    /**
         * An error occurred.
         
    */
        
    public void addError(Test test, Throwable t);
       
    /**
         * A failure occurred.
         
    */
         
    public void addFailure(Test test, Throwable t);  
       
    /**
         * A test ended.
         
    */
         
    public void endTest(Test test); 
       
    /**
         * A test started.
         
    */
        
    public void startTest(Test test);
    }

        接口清晰易懂,就是一系列將測試過程的信息傳遞給觀察者的操作。具體的子類將接受這些信息,并按照它們的方式顯示給用戶。
         比如,我們看看swing的UI中的TestRunner,它將這些信息顯示在一個swing寫的UI界面上:
        public void startTest(Test test) {
            showInfo(
    "Running: "+test);
        }

        public void addError(Test test, Throwable t) {
            fNumberOfErrors.setText(Integer.toString(fTestResult.errorCount()));
            appendFailure(
    "Error", test, t);
        }
        
    public void addFailure(Test test, Throwable t) {
            fNumberOfFailures.setText(Integer.toString(fTestResult.failureCount()));
            appendFailure(
    "Failure", test, t);
        }
        public void endTest(Test test) {
            setLabelValue(fNumberOfRuns, fTestResult.runCount());
            fProgressIndicator.step(fTestResult.wasSuccessful());
        }

    可以看到,它將錯誤信息,異常信息保存在List或者Vector集合內,然后顯示在界面上:
    private void showErrorTrace() {
            
    int index= fFailureList.getSelectedIndex();
            
    if (index == -1)
                
    return;
        
            Throwable t
    = (Throwable) fExceptions.elementAt(index);
            
    if (fTraceFrame == null) {
                fTraceFrame
    = new TraceFrame();
                fTraceFrame.setLocation(
    100100);
               }
            fTraceFrame.showTrace(t);
            fTraceFrame.setVisible(
    true);
        }
        
    private void showInfo(String message) {
            fStatusLine.setFont(PLAIN_FONT);
            fStatusLine.setForeground(Color.black);
            fStatusLine.setText(message);
        }
        
    private void showStatus(String status) {
            fStatusLine.setFont(BOLD_FONT);
            fStatusLine.setForeground(Color.red);
            fStatusLine.setText(status);
        }

    而Junit中的目標對象(Subject)就是TestResult對象,它有添加觀察者的方法:

    /**
         * Registers a TestListener
         
    */
        
    public synchronized void addListener(TestListener listener) {
            fListeners.addElement(listener);
        }

    而通知觀察者又是怎么做的呢?請看這幾個方法,都是循環遍歷觀察者列表,并調用相應的更新方法:

    /**
         * Adds an error to the list of errors. The passed in exception caused the
         * error.
         
    */
        
    public synchronized void addError(Test test, Throwable t) {
            fErrors.addElement(
    new TestFailure(test, t));
            
    for (Enumeration e = fListeners.elements(); e.hasMoreElements();) {
                ((TestListener) e.nextElement()).addError(test, t);
            }
        }

        
    /**
         * Adds a failure to the list of failures. The passed in exception caused
         * the failure.
         
    */
        
    public synchronized void addFailure(Test test, AssertionFailedError t) {
            fFailures.addElement(
    new TestFailure(test, t));
            
    for (Enumeration e = fListeners.elements(); e.hasMoreElements();) {
                ((TestListener) e.nextElement()).addFailure(test, t);
            }
        }

        
    /**
         * Registers a TestListener
         
    */
        
    public synchronized void addListener(TestListener listener) {
            fListeners.addElement(listener);
        }

        
    /**
         * Informs the result that a test was completed.
         
    */
        
    public synchronized void endTest(Test test) {
            
    for (Enumeration e = fListeners.elements(); e.hasMoreElements();) {
                ((TestListener) e.nextElement()).endTest(test);
            }
        }


    使用這個模式后帶來的好處:
    1)上面提到的Subject與Observer的抽象耦合,使JUnit可以支持不同的使用方式
    2)支持了廣播通信,目標對象不關心有多少對象對自己注冊,它只是通知注冊的觀察者

    最后,我實現了一個簡單的ConsoleRunner,在控制臺執行JUnit,比如我們寫了一個簡單測試:
    package junit.samples;

    import junit.framework.*;

    /**
     * Some simple tests.
     * 
     
    */
    public class SimpleTest extends TestCase {
        
    protected int fValue1;

        
    protected int fValue2;

        
    public SimpleTest(String name) {
            
    super(name);
        }

        
    public void setUp() {
            fValue1 
    = 2;
            fValue2 
    = 3;
        }

        
    public void testAdd() {
            
    double result = fValue1 + fValue2;
            
    assert(result == 5);
        }


        
    public void testEquals() {
            assertEquals(
    1212);
            assertEquals(
    12L12L);
            assertEquals(
    new Long(12), new Long(12));

            assertEquals(
    "Size"1212);
            assertEquals(
    "Capacity"12.011.990.01);
        }
    }

    使用ConsoleRunner調用這個測試,代碼很簡單,不多做解釋了:
    package net.rubyeye.junit.framework;

    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Vector;

    import junit.framework.Test;
    import junit.framework.TestListener;
    import junit.framework.TestResult;
    import junit.samples.SimpleTest;
    //實現觀察者接口
    public class ConsoleRunner implements TestListener {

        
    private TestResult fTestResult;

        
    private Vector fExceptions;

        
    private Vector fFailedTests;

        
    private List fFailureList;

        
    public ConsoleRunner() {
            fExceptions 
    = new Vector();
            fFailedTests 
    = new Vector();
            fFailureList 
    = new ArrayList();
        }

        
    public void endTest(Test test) {
            System.out.println(
    "測試結束:");
            String message 
    = test.toString();
            
    if (fTestResult.wasSuccessful())
                System.out.println(message 
    + " 測試成功!");
            
    else if (fTestResult.errorCount() == 1)
                System.out.println(message 
    + " had an error");
            
    else
                System.out.println(message 
    + " had a failure");

            
    for (int i = 0; i < fFailureList.size(); i++) {
                System.out.println(fFailureList.get(i));
            }
            
    for (int i = 0; i < fFailedTests.size(); i++) {
                System.out.println(fFailureList.get(i));
            }
            
    for (int i = 0; i < fExceptions.size(); i++) {
                System.out.println(fFailureList.get(i));
            }

            System.out.println(
    "------------------------");
        }

        
    public void startTest(Test test) {
            System.out.println(
    "開始測試:" + test);
        }

        
    public static TestResult createTestResult() {
            
    return new TestResult();
        }

        
    private String truncateString(String s, int length) {
            
    if (s.length() > length)
                s 
    = s.substring(0, length) + "";
            
    return s;
        }

        
    public void addError(Test test, Throwable t) {
            System.out.println(fTestResult.errorCount());
            appendFailure(
    "Error", test, t);
        }

        
    public void addFailure(Test test, Throwable t) {
            System.out.println(fTestResult.failureCount());
            appendFailure(
    "Failure", test, t);
        }

        
    private void appendFailure(String kind, Test test, Throwable t) {
            kind 
    += "" + test;
            String msg 
    = t.getMessage();
            
    if (msg != null) {
                kind 
    += ":" + truncateString(msg, 100);
            }
            fFailureList.add(kind);
            fExceptions.addElement(t);
            fFailedTests.addElement(test);
        }

        
    public void go(String args[]) {
            Method[] methods 
    = SimpleTest.class.getDeclaredMethods();

            
    for (int i = 0; i < methods.length; i++) {
                
    //取所有以test開頭的方法
                if (methods[i].getName().startsWith("test")) {
                    Test test 
    = new SimpleTest(methods[i].getName());
                    fTestResult 
    = createTestResult();
                    fTestResult.addListener(ConsoleRunner.
    this);
                    
    //執行測試
                    test.run(fTestResult);
                }
            }

        }

        
    public static void main(String args[]) {
            
    new ConsoleRunner().go(args);
        }

    }

    主站蜘蛛池模板: 亚洲精品国产av成拍色拍| 97在线免费视频| 亚洲精品第一国产综合精品99 | 黄色网址免费观看| 三年片免费高清版 | 日韩精品视频免费在线观看| 国产在线国偷精品免费看| 亚洲日韩亚洲另类激情文学| 亚洲日本一区二区| 亚洲综合婷婷久久| 亚洲黄色在线电影| 亚洲国产精品白丝在线观看| 亚洲色图在线观看| 亚洲精品视频专区| 亚洲成人免费在线观看| 亚洲第一精品电影网| 2020天堂在线亚洲精品专区| 亚洲欧洲春色校园另类小说| 亚洲网红精品大秀在线观看| 亚洲国产精品午夜电影| 日韩欧美亚洲中文乱码| 无码免费又爽又高潮喷水的视频 | 亚洲真人无码永久在线观看| 欧美亚洲国产SUV| 东方aⅴ免费观看久久av| 亚欧日韩毛片在线看免费网站| 亚洲电影免费观看| 亚洲精品无码久久不卡| 亚洲色图视频在线观看| 亚洲欧洲AV无码专区| 在线观看人成视频免费无遮挡| 99国产精品视频免费观看| 日韩成人免费aa在线看| 亚洲成a人片77777老司机| 亚洲中文字幕乱码熟女在线| 爽爽爽爽爽爽爽成人免费观看| 曰曰鲁夜夜免费播放视频| 亚洲欧洲日产国码高潮αv| 久久夜色精品国产噜噜亚洲a| 精品一区二区三区免费观看| 成人午夜视频免费|