策略模式

今天和大家分享一下設(shè)計(jì)模式中的策略模式纠吴,這里只是分享樓主自己的見解,如有考慮不恰當(dāng)?shù)牡胤交哿觯€請理解戴已,那么我們言歸正傳固该。由于樓主自己工作的原因,常常需要將數(shù)據(jù)庫中資源數(shù)據(jù)生成相應(yīng)的靜態(tài)化文件(json文件)糖儡,也就是俗稱的打包伐坏,來給前端調(diào)用。資源數(shù)據(jù)可能有很多種類型休玩。例如:新聞著淆、電影、小說拴疤、動漫永部、游戲等。不同的類型呐矾,在打包時苔埋,可能有不一樣的流程,例如新聞和電影就有很大的不同蜒犯,新聞在打包時组橄,除了基本的流程外,還需要有上傳功能罚随,也就是自動下發(fā)玉工,因?yàn)樾侣劦膶?shí)效性要求很高。但電影就不需要有此功能淘菩,因?yàn)榇虬募笞癜啵荒茏詣酉掳l(fā),需人工審批潮改。下面我們按照上面的需求用一般的思維來設(shè)計(jì)我們相關(guān)的類狭郑。

按照需求我們需要一個接口來定義我們打包的所有方法,然后我們?yōu)樵摻涌趧?chuàng)建相應(yīng)的實(shí)現(xiàn)類將公共的可以復(fù)用的方法封裝到這個實(shí)現(xiàn)類中汇在,將每個模塊特有的方法放到它的子類里去自己實(shí)現(xiàn)翰萨。下面為具體的代碼:

/**
 * 定義所有打包的流程
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 10:45
 * @since 1.0.0
 */
public interface Staticize {

    /**
     * 初始化 如:創(chuàng)建相應(yīng)文件夾、定義一些打包參數(shù)等
     */
    void doInit();

    /**
     * 獲取數(shù)據(jù)庫資源數(shù)據(jù)
     */
    void findStaticData();

    /**
     * 獲取相應(yīng)的資源 如 圖片糕殉、音頻等
     */
    void doGetRes();

    /**
     * 將生成的資源包打包ZIP文件
     */
    void doZip();

    /**
     * 將ZIP文件自動下發(fā) (只有新聞有此流程)
     */
    void doUpload();
}
/**
 * 公共流程方法實(shí)現(xiàn)
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:11
 * @since 1.0.0
 */
public class DetailStaticize implements Staticize {
    public void doInit() {
        System.out.println("初始化成功");
    }

    public void findStaticData() {
        System.out.println("獲取新聞資源數(shù)據(jù)成功");
        System.out.println("獲取電影資源數(shù)據(jù)成功");
    }

    public void doGetRes() {
        System.out.println("獲取相應(yīng)的資源成功");
    }

    public void doZip() {
        System.out.println("ZIP創(chuàng)建成功");
    }

    public void doUpload() {
    }
}
/**
 * 新聞特有流程方法處理
 * 
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:13
 * @since 1.0.0
 */
public class NewsStaticize extends DetailStaticize {

    @Override
    public void findStaticData() {
        System.out.println("獲取新聞資源數(shù)據(jù)成功");
    }

    @Override
    public void doUpload() {
        System.out.println("新聞自動下發(fā)成功");
    }
}
/**
 * 電影特有流程方法處理
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:13
 * @since 1.0.0
 */
public class VideoStaticize extends DetailStaticize {
    @Override
    public void findStaticData() {
        System.out.println("獲取電影資源數(shù)據(jù)成功");
    }

    @Override
    public void doUpload() {
    }
}

下面為測試結(jié)果 調(diào)用默認(rèn)接口實(shí)現(xiàn):

/**
 * 測試類
 * 
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:15
 * @since 1.0.0
 */
public class Test {
    @org.junit.Test
    public void test() {
        Staticize staticize = new DetailStaticize();
        staticize.doInit();
        staticize.findStaticData();
        staticize.doGetRes();
        staticize.doZip();
        staticize.doUpload();
    }
}

結(jié)果為:

初始化成功
獲取新聞資源數(shù)據(jù)成功
獲取電影資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功

調(diào)用新聞實(shí)現(xiàn):

/**
 * 測試類
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:15
 * @since 1.0.0
 */
public class Test {
    @org.junit.Test
    public void test() {
        Staticize staticize = new NewsStaticize();
        staticize.doInit();
        staticize.findStaticData();
        staticize.doGetRes();
        staticize.doZip();
        staticize.doUpload();
    }
}

結(jié)果為:

初始化成功
獲取新聞資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功
新聞自動下發(fā)成功
調(diào)用電影實(shí)現(xiàn):
/**
 * 測試類
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:15
 * @since 1.0.0
 */
public class Test {
    @org.junit.Test
    public void test() {
        Staticize staticize = new VideoStaticize();
        staticize.doInit();
        staticize.findStaticData();
        staticize.doGetRes();
        staticize.doZip();
        staticize.doUpload();
    }
}

結(jié)果為:

初始化成功
獲取電影資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功

按照執(zhí)行結(jié)果來看亩鬼,似乎實(shí)現(xiàn)了我們的需求。但有一點(diǎn)需要注意就是我們的子類里會有一些空實(shí)現(xiàn)阿蝶,也就是電影子類重寫父類的upload()方法辛孵。如果我們的另一個模塊,例如 綜藝模塊赡磅,由于資源也是比較大的魄缚,不需要自動下發(fā),那我們也要在此模塊中添加upload方法的空實(shí)現(xiàn),可能有些人認(rèn)為冶匹,只是一個空實(shí)現(xiàn)习劫,沒啥大不了的,畢竟什么也沒有寫嗎嚼隘。但是诽里,如果需求變了,需要電影或者綜藝等沒有自動下發(fā)功能的模塊當(dāng)ZIP文件生成成功時自動發(fā)郵件通知運(yùn)營手動拷貝呢飞蛹,這時我們就要在這個upload()方法里添加具體的發(fā)送郵件代碼了谤狡,并且這兩個模塊的業(yè)務(wù)邏輯完全相同的,這就造成了代碼的重復(fù)卧檐,不能復(fù)用墓懂。這時,可能用人還會說霉囚,我可以寫一個工具類來處理發(fā)送郵件的需求捕仔。這樣就解決了,代碼重復(fù)盈罐,不能復(fù)用的問題榜跌。這樣當(dāng)然可以,但是還有一點(diǎn)別忘了盅粪,就是調(diào)用發(fā)送郵件的方法還是要寫在每一個具體的子類中钓葫,這個當(dāng)工具類做某些修改,那么和它有關(guān)的所有的調(diào)用類都要做出相應(yīng)修改票顾,這就顯然也不是最好的解決方案础浮。

那怎么辦呢,答案你懂的库物,就是用策略模式霸旗。那什么是策略模式呢?

策略模式的定義:定義了算法組贷帮,分別封裝起來戚揭,讓它們之間可以互相替換,此模式讓算法的變化獨(dú)立于使用算法的客戶撵枢。

按照設(shè)計(jì)模式的基本原則之一:找出應(yīng)用中可能變化之處民晒,把它們獨(dú)立出來,不要和那些不需要變化的代碼混在一起锄禽。也就是說潜必,我們需要將接口中需要變化的地方提取出來。按照我們的需求也就是需要將upload()方法提取出來沃但。那怎么提取呢磁滚,是提取成接口還是提取成一個單獨(dú)的類?這個還有一個設(shè)計(jì)模式的原則就是:針對接口編程,而不是針對實(shí)現(xiàn)編程垂攘。這句話的意思是:我們知道一個接口可以有很多個實(shí)現(xiàn)類维雇,至于一共有哪些實(shí)現(xiàn)類,接口是不需要知道的晒他,調(diào)用時只要實(shí)例化這個實(shí)現(xiàn)類并指向該接口就可執(zhí)行調(diào)用吱型,如果有新的實(shí)現(xiàn)類,只要新實(shí)例化這個新類即可陨仅,對接口方法的調(diào)用沒有改變津滞,這樣就很方便擴(kuò)展。

回到我們的需求上灼伤,我們需要將upload抽取成一個接口触徐,然后為了它創(chuàng)建兩個實(shí)現(xiàn)類,一個為自動下發(fā)實(shí)現(xiàn)饺蔑,一個為不需要自動下發(fā)實(shí)現(xiàn)锌介。具體代碼如下:

/**
 * 下發(fā)接口
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 11:06
 * @since 1.0.0
 */
public interface UploadStaticize {
    void upload();
}
/**
 * 自動下發(fā)接口實(shí)現(xiàn)
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 13:26
 * @since 1.0.0
 */
public class DefalutUploadStaticize implements UploadStaticize {
    public void upload() {
        System.out.println("upload");
    }
}
/**
 * 不需要自動下發(fā)接口實(shí)現(xiàn)
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 13:27
 * @since 1.0.0
 */
public class DefalutUnUploadStaticize implements UploadStaticize {
    public void upload() {
        System.out.println("un upload");
    }
}

DefaultStaticize實(shí)現(xiàn)類修改如下:

/**
 * 公共流程方法實(shí)現(xiàn)
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:11
 * @since 1.0.0
 */
public class DetailStaticize implements Staticize {

    UploadStaticize uploadStaticize = new DefalutUnUploadStaticize();

    public void doInit() {
        System.out.println("初始化成功");
    }

    public void findStaticData() {
        System.out.println("獲取新聞資源數(shù)據(jù)成功");
        System.out.println("獲取電影資源數(shù)據(jù)成功");
    }

    public void doGetRes() {
        System.out.println("獲取相應(yīng)的資源成功");
    }

    public void doZip() {
        System.out.println("ZIP創(chuàng)建成功");
    }

    public void doUpload() {
        uploadStaticize.upload();
    }
}

NewsStaticize實(shí)現(xiàn)類修改如下:

/**
 * 新聞特有流程方法處理
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:13
 * @since 1.0.0
 */
public class NewsStaticize extends DetailStaticize {

    public NewsStaticize() {
        uploadStaticize = new DefalutUploadStaticize();
    }

    @Override
    public void findStaticData() {
        System.out.println("獲取新聞資源數(shù)據(jù)成功");
    }
}

測試類代碼未修改:新聞模塊

/**
 * 測試類
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:15
 * @since 1.0.0
 */
public class Test {
    @org.junit.Test
    public void test() {
        Staticize staticize = new NewsStaticize();
        staticize.doInit();
        staticize.findStaticData();
        staticize.doGetRes();
        staticize.doZip();
        staticize.doUpload();
    }
}

測試結(jié)果:

初始化成功
獲取新聞資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功
upload

電影模塊:

/**
 * 測試類
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:15
 * @since 1.0.0
 */
public class Test {
    @org.junit.Test
    public void test() {
        Staticize staticize = new VideoStaticize();
        staticize.doInit();
        staticize.findStaticData();
        staticize.doGetRes();
        staticize.doZip();
        staticize.doUpload();
    }
}

測試結(jié)果:

初始化成功
獲取電影資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功
un upload

這樣寫有有什么好處呢,就是方便擴(kuò)展猾警,如我上面所說如果需要將沒有自動下發(fā)功能的模塊添加發(fā)送郵件通知功能孔祸,那么我們只需要新創(chuàng)建一個UploadStaticize的子類就可以了。對應(yīng)修改如下:

/**
 * 發(fā)送郵件實(shí)現(xiàn)類
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 17:57
 * @since 1.0.0
 */
public class EmailUploadStaticize implements UploadStaticize {
    public void upload() {
        System.out.println("send email");
    }
}
/**
 * 電影特有流程方法處理
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:13
 * @since 1.0.0
 */
public class VideoStaticize extends DetailStaticize {
    public VideoStaticize() {
        uploadStaticize = new EmailUploadStaticize();
    }

    @Override
    public void findStaticData() {
        System.out.println("獲取電影資源數(shù)據(jù)成功");
    }

}

測試類代碼不變 发皿,輸入結(jié)果為:

初始化成功
獲取電影資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功
send email

結(jié)果似乎已經(jīng)很完美了崔慧,但你可能會發(fā)現(xiàn) ,就是我們改動了VideoStaticize 這個類穴墅,這個是電影模塊的核心類惶室,我們不能隨隨便便就修改已經(jīng)開發(fā)好的類,因?yàn)檫@樣就不是可擴(kuò)展易維護(hù)的程序了玄货,并且這也不符合設(shè)計(jì)模式的基本原則皇钞。那什么辦法嗎,答案還是有的松捉。就是我們讓客戶端可以設(shè)置夹界,如果客戶端不設(shè)置那么程序就按照原先的邏輯執(zhí)行,如果設(shè)置了隘世,就按照新設(shè)置的策略執(zhí)行可柿。具體代碼如下:

Staticize接口新增setUploadStaticize()方法:

/**
 * 定義所有打包的流程
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 10:45
 * @since 1.0.0
 */
public interface Staticize {

    /**
     * 初始化 如:創(chuàng)建相應(yīng)文件夾、定義一些打包參數(shù)等
     */
    void doInit();

    /**
     * 獲取數(shù)據(jù)庫資源數(shù)據(jù)
     */
    void findStaticData();

    /**
     * 獲取相應(yīng)的資源 如 圖片丙者、音頻等
     */
    void doGetRes();

    /**
     * 將生成的資源包打包ZIP文件
     */
    void doZip();

    /**
     * 將ZIP文件自動下發(fā) (只有新聞有此流程)
     */
    void doUpload();

    /**
     * 添加可以動態(tài)設(shè)置UploadStaticize的方法
     *
     * @param uploadStaticzie
     */
    void setUploadStaticzie(UploadStaticize uploadStaticzie);
}

添加setUploadStaticize()方法具體的實(shí)現(xiàn):

/**
 * 公共流程方法實(shí)現(xiàn)
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:11
 * @since 1.0.0
 */
public class DetailStaticize implements Staticize {

    UploadStaticize uploadStaticize = new DefalutUploadStaticize();

    public void doInit() {
        System.out.println("初始化成功");
    }

    public void findStaticData() {
        System.out.println("獲取新聞資源數(shù)據(jù)成功");
        System.out.println("獲取電影資源數(shù)據(jù)成功");
    }

    public void doGetRes() {
        System.out.println("獲取相應(yīng)的資源成功");
    }

    public void doZip() {
        System.out.println("ZIP創(chuàng)建成功");
    }

    public void doUpload() {
        uploadStaticize.upload();
    }

    public void setUploadStaticzie(UploadStaticize uploadStaticzie) {
        this.uploadStaticize = uploadStaticzie;
    }
}

測試類代碼修改:

/**
 * 測試類
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:15
 * @since 1.0.0
 */
public class Test {
    @org.junit.Test
    public void test() {
        Staticize staticize = new NewsStaticize();
        staticize.doInit();
        staticize.findStaticData();
        staticize.doGetRes();
        staticize.doZip();
        staticize.setUploadStaticzie(new EmailUploadStaticize());
        staticize.doUpload();
    }
}

執(zhí)行結(jié)果:

初始化成功
獲取新聞資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功
send email

這樣程序就是一個可以擴(kuò)展易維護(hù)的程序了复斥。例如 如果需求變更需要新聞模塊不但自動下發(fā)還要發(fā)送郵件怎么辦呢。這時我們就不需要做任何開發(fā)了械媒,只要客戶端自己調(diào)用就可以了目锭。代碼如下:

/**
 * 測試類
 *
 * @author Sama
 * @author jilinwula@foxmail.com
 * @date 2017-01-04 15:15
 * @since 1.0.0
 */
public class Test {
    @org.junit.Test
    public void test() {
        Staticize staticize = new NewsStaticize();
        staticize.doInit();
        staticize.findStaticData();
        staticize.doGetRes();
        staticize.doZip();
        staticize.doUpload();
        staticize.setUploadStaticzie(new EmailUploadStaticize());
        staticize.doUpload();
    }
}

執(zhí)行結(jié)果:

初始化成功
獲取新聞資源數(shù)據(jù)成功
獲取相應(yīng)的資源成功
ZIP創(chuàng)建成功
upload
send email

這就是我對設(shè)計(jì)模式中策略模式的理解 ,如本文有不正確之處,歡迎指出痢虹。謝謝键俱。

原文地址:吉林烏拉

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市世分,隨后出現(xiàn)的幾起案子编振,更是在濱河造成了極大的恐慌,老刑警劉巖臭埋,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踪央,死亡現(xiàn)場離奇詭異,居然都是意外死亡瓢阴,警方通過查閱死者的電腦和手機(jī)畅蹂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荣恐,“玉大人液斜,你說我怎么就攤上這事〉拢” “怎么了少漆?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長硼被。 經(jīng)常有香客問我示损,道長,這世上最難降的妖魔是什么嚷硫? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任检访,我火速辦了婚禮,結(jié)果婚禮上仔掸,老公的妹妹穿的比我還像新娘脆贵。我一直安慰自己,他們只是感情好起暮,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布卖氨。 她就那樣靜靜地躺著,像睡著了一般鞋怀。 火紅的嫁衣襯著肌膚如雪双泪。 梳的紋絲不亂的頭發(fā)上持搜,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天密似,我揣著相機(jī)與錄音,去河邊找鬼葫盼。 笑死残腌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抛猫,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蟆盹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了闺金?” 一聲冷哼從身側(cè)響起逾滥,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎败匹,沒想到半個月后寨昙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掀亩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年舔哪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片槽棍。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡捉蚤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炼七,到底是詐尸還是另有隱情缆巧,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布豌拙,位于F島的核電站盅蝗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏姆蘸。R本人自食惡果不足惜墩莫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逞敷。 院中可真熱鬧狂秦,春花似錦、人聲如沸推捐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牛柒。三九已至堪簿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間皮壁,已是汗流浹背椭更。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛾魄,地道東北人虑瀑。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓湿滓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舌狗。 傳聞我的和親對象是個殘疾皇子叽奥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容