測試工具中的設計模式實例談 - 策略模式(Strategy)II

本文介紹來自Database-Rider中關于數據庫導入時的策略模式案例

首先來看一下使用案例

    @Test
    @DataSet(value = "datasets/yml/users.yml", strategy = SeedStrategy.CLEAN_INSERT)
    public void shouldSeedDataSetDisablingContraints() {
        User user = (User) EntityManagerProvider.em().createQuery("select u from User u where u.id = 1").getSingleResult();
        assertThat(user).isNotNull();
        assertThat(user.getId()).isEqualTo(1);
    }

從上述代碼中可以了解到,@DataSet這個注解用于在測試用例執行前將來自value屬性指定的數據文件users.yml插入到數據庫中,通過strategy屬性來指定數據庫的插入方式為先清空數據庫文件中涉及到的目標表,然后插入數據文件中提供的數據。

來看一下有哪些策略可供使用

public enum SeedStrategy {
    CLEAN_INSERT(DatabaseOperation.CLEAN_INSERT),
    TRUNCATE_INSERT(new CompositeOperation(DatabaseOperation.TRUNCATE_TABLE, DatabaseOperation.INSERT)),
    INSERT(DatabaseOperation.INSERT),
    REFRESH(DatabaseOperation.REFRESH),
    UPDATE(DatabaseOperation.UPDATE);
}

命名上看,各種操作還是非常容易理解的,這里就不展開說明了。不過其中的CompositeOperation說明這其中還使用了組合模式,這個可以在后續解讀。

策略抽象類

策略模式通過一個接口或者抽象類來定義策略的運行方法。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.dbunit.operation;

import java.sql.SQLException;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;

public abstract class DatabaseOperation {
    public static final DatabaseOperation NONE = new DatabaseOperation.DummyOperation();
    public static final DatabaseOperation UPDATE = new UpdateOperation();
    public static final DatabaseOperation INSERT = new InsertOperation();
    public static final DatabaseOperation REFRESH = new RefreshOperation();
    public static final DatabaseOperation DELETE = new DeleteOperation();
    public static final DatabaseOperation DELETE_ALL = new DeleteAllOperation();
    public static final DatabaseOperation TRUNCATE_TABLE = new TruncateTableOperation();
    public static final DatabaseOperation CLEAN_INSERT;

    public DatabaseOperation() {
    }

    public static final DatabaseOperation TRANSACTION(DatabaseOperation operation) {
        return new TransactionOperation(operation);
    }

    public static final DatabaseOperation CLOSE_CONNECTION(DatabaseOperation operation) {
        return new CloseConnectionOperation(operation);
    }

    public abstract void execute(IDatabaseConnection var1, IDataSet var2) throws DatabaseUnitException, SQLException;

    static {
        CLEAN_INSERT = new CompositeOperation(DELETE_ALL, INSERT);
    }

    private static class DummyOperation extends DatabaseOperation {
        private DummyOperation() {
        }

        public void execute(IDatabaseConnection connection, IDataSet dataSet) {
        }
    }
}

在此案例中,使用了抽象類的方式,并提供了execute這個抽象方法。InsertOperation、UpdateOperation等具體策略只要繼承并實現上述方法即可。

來看一下這個案例的類圖(部分)


image.png

從類圖上看,@DataSet注解使用到的數據庫操作屬于CUD部分,也就是從AbstractOperation這個抽象類繼承而來的部分。

數據庫導入操作類

策略模式中,一般都會有一個Context類來作為使用某種策略的類。
DBRider定義了一個DataSetExecutorImpl,用于實現對數據庫的各項操作。其中用于處理@DataSet注解的方法是createDataSet。

@Override
    public void createDataSet(DataSetConfig dataSetConfig) {
 //......
     DatabaseOperation operation = getOperation(dataSetConfig);
 operation.execute(getRiderDataSource().getDBUnitConnection(), resultingDataSet);

這其中,通過getOperation方法來獲取具體的DatebaseOperation策略類,并執行其中的execute方法。

獲取策略的工廠方法

而在實際開發中為了將具體策略類的創建和使用者隔離,還會結合工廠模式。在DBRider這個案例中,只是簡單地使用了getOperation方法。
具體代碼如下:

private DatabaseOperation getOperation(DataSetConfig dataSetConfig) {
        SeedStrategy strategy = dataSetConfig.getstrategy();
        if (getRiderDataSource().getDBType() == RiderDataSource.DBType.MSSQL && dataSetConfig.isFillIdentityColumns()) {
            switch (strategy) {
                case INSERT:
                    return InsertIdentityOperation.INSERT;
                case REFRESH:
                    return InsertIdentityOperation.REFRESH;
                case CLEAN_INSERT:
                    return InsertIdentityOperation.CLEAN_INSERT;
                case TRUNCATE_INSERT:
                    return new CompositeOperation(DatabaseOperation.TRUNCATE_TABLE,
                            InsertIdentityOperation.INSERT);
            }
        }
        return strategy.getOperation();
    }
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374