天天看點

JUnit源碼分析(四)——從Decorator模式說起

其實我這系列小文,名為源碼分析,其實是自己讀《設計模式》的讀書筆記。decorator模式在java的io庫中得到應用,java的io庫看起來複雜,其實了解了decorator模式再回頭看可以很好了解并使用。

    decorator模式,也就是裝飾器模式,是對象結構型模式之一。

1.意圖:動态地給一個對象添加一些額外的職責。給對象添加功能,我們首先想到的是繼承,但是如果每增一個功能都需要繼承,類的繼承體系将無可避免地變的龐大和難以了解。面向對象設計的原則:優先使用組合,而非繼承,繼承的層次深度最好不過三。

2.适用場景:

1)在不影響其他對象的情況下,以動态、透明的方式給單個對象添加額外的責任

2)處理可以撤銷的職責

3)為了避免類的數目爆炸,或者不能采用生成子類的方法進行擴充時

3.uml圖和協作:

JUnit源碼分析(四)——從Decorator模式說起

component——定義一個對象接口,可以給這些對象動态地添加職責

concretecomponent——定義一個對象,可以給這個對象添加職責

decorator——維持一個指向component的引用,并定義一個與component一緻的接口,作為裝飾類的父類

concretedecorator——具體裝飾類

4.效果:

1)與靜态繼承相比,decorator可以動态添加職責,更為靈活

2)避免産生複雜的類,通過動态添加職責,而不是一次性提供一個萬能的接口

3)缺點是将産生比較多的小對象,對學習上有難度,顯然,java.io就是這個問題

我們以一個例子來實作decorator模式,假設這樣一個場景:在某個應用中需要列印票據,我們寫了一個printticket接口,然後提供一個實作類(defaultprintticket)實作列印的功能:

package com.rubyeye.design_pattern.decorator;

//抽象component接口

public interface printticket {

    public void print();

}

//預設實作類,列印票據

public class defaultprintticket implements printticket {

    public void print() {

        system.out.println("ticket body");

    }

ok,我們的功能已經實作,我們還展現了針對接口程式設計的原則,替換一個新的列印方式很靈活,但是客戶開始提需求了——人生無法避免的三件事:交稅、死亡和需求變更。客戶要求列印頁眉,你首先想到的是繼承:

public class anotherprintticket implements printticket {

        system.out.println("ticket header");

請注意,我們這裡隻是簡單的示例,在實際項目中也許意味着添加一大段代碼,并且需要修改列印票據本體的功能。需求接踵而至,客戶要求添加列印頁碼,要求增加列印花紋,要求可以聯打......你的類越來越龐大,直到你看見這個類都想吐的地步!-_-。讓我們看看另一個方案,使用decorator模式來動态地給列印增加一些功能,首先是實作一個decorator,它需要保持一個到printticket接口的引用:

public class printticketdecorator implements printticket {

    protected printticket printticket;

    public printticketdecorator(printticket printticket) {

        this.printticket = printticket;

    //預設調用printticket的print

        printticket.print();

然後,我們實作兩個具體的裝飾類——列印頁眉和頁腳:

public class headerprintticket extends printticketdecorator {

    public headerprintticket(printticket printticket){

        super(printticket);

        super.print();

public class footerprintticket extends printticketdecorator {

    public footerprintticket(printticket printticket) {

        system.out.println("ticket footer");

    使用起來也很容易:

public class decoratortest {

    /**

     * @param args

     */

    public static void main(string[] args) {

        printticket print=new headerprintticket(new footerprintticket(new defaultprintticket()));

        print.print();

輸出:

ticket header

ticket body

ticket footer

    了解了decorator模式,我們聯系了下junit裡面的應用。作為一個測試架構,應該友善地支援二次開發,也許使用者開發自己的testcase,添加自定義的功能,比如執行重複測試、多線程測試等等。動态添加職責,而又不想使用靜态繼承,這正是decorator使用的地方。在junit.extensions包中有一個testdecorator,正是所有裝飾類的父類,也是作為二次開發的基礎,它實作了test接口,而test接口就是我們定義的抽象接口:

public class testdecorator implements  test {

    //保有一個指向test的引用

        protected test ftest;

    public testdecorator(test test) {

        ftest= test;

JUnit源碼分析(四)——從Decorator模式說起

        public void basicrun(testresult result) {

          ftest.run(result);

        }

        public void run(testresult result) {

           basicrun(result);

        }

JUnit源碼分析(四)——從Decorator模式說起

junit已經提供了兩個裝飾類:junit.extensions.activetest用于處理多線程,junit.extensions.repeatedtest用于執行重複測試,看看repeatedtest是怎麼實作的:

public class repeatedtest extends  testdecorator {

        //重複次數

    private int ftimesrepeat;

    public repeatedtest(test test, int repeat) {

        super(test);

        ftimesrepeat= repeat;

        //重複執行

                for (int i= 0; i < ftimesrepeat; i++) {

            if (result.shouldstop())

                break;

            super.run(result);

        }

JUnit源碼分析(四)——從Decorator模式說起
JUnit源碼分析(四)——從Decorator模式說起

    repeatedtest繼承testdecorator ,覆寫run(testreult result)方法,重複執行,super.run(result)将調用傳入的testcase的run(testresult result)方法,這已經在testdecorator預設實作。看看使用方式,使用裝飾模式的好處不言而喻。

testsuite suite = new testsuite();

suite.addtest(new testsetup(new repeatedtest(new testmath("testadd"),12)));

文章轉自莊周夢蝶  ,原文釋出時間5.17

繼續閱讀