設(shè)計(jì)模式2:策略模式

今天來講一講“策略模式”(Strategy Pattern)。為什么第二個(gè)講它呢?單例模式第一個(gè)講是我開始以為它最簡(jiǎn)單(其實(shí)并不是),策略模式第二個(gè)講是因?yàn)椤禜ead First Design Patterns》這本書第一個(gè)講的就是策略模式。

什么是策略模式?

策略模式就是定義算法族,把它們封裝起來,讓它們可相互替換,在用的時(shí)候再?zèng)Q定用哪一個(gè)算法(原文:Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it)。
這個(gè)定義可能比較難以理解。以我的理解,所謂策略,就是隨機(jī)應(yīng)變,根據(jù)具體情況作出最佳選擇。
這樣做的好處是什么?
最大的好處就是代碼靈活。按照《Head First Design Patterns》所說,要把不變的東西保留復(fù)用,變的東西抽出來。策略模式中,父類只負(fù)責(zé)調(diào)用接口函數(shù),而不關(guān)心具體是怎么實(shí)現(xiàn)的,并且利用多態(tài)可以自由選擇實(shí)現(xiàn)接口類。

代碼例子

定義一個(gè)屠龍勇士對(duì)象,應(yīng)用屠龍接口,這個(gè)接口有三個(gè)候選策略:近戰(zhàn),遠(yuǎn)程和法術(shù)。
屠龍接口:

@FunctionalInterface
public interface DragonSlayingStrategy {

  void execute();

}

屠龍勇士類:

public class DragonSlayer {

  private DragonSlayingStrategy strategy;

  public DragonSlayer(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }

  public void changeStrategy(DragonSlayingStrategy strategy) {
    this.strategy = strategy;
  }

  public void goToBattle() {
    strategy.execute();
  }
}

近戰(zhàn)接口實(shí)現(xiàn)類(其余兩個(gè)類似就省略了):

public class MeleeStrategy implements DragonSlayingStrategy {

  private static final Logger LOGGER = LoggerFactory.getLogger(MeleeStrategy.class);

  @Override
  public void execute() {
    LOGGER.info("With your Excalibur you sever the dragon's head!");
  }
}

測(cè)試:

public static void main(String[] args) {
    // GoF Strategy pattern
    LOGGER.info("Green dragon spotted ahead!");
    DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy());
    dragonSlayer.goToBattle();
    LOGGER.info("Red dragon emerges.");
    dragonSlayer.changeStrategy(new ProjectileStrategy());
    dragonSlayer.goToBattle();
    LOGGER.info("Black dragon lands before you.");
    dragonSlayer.changeStrategy(new SpellStrategy());
    dragonSlayer.goToBattle();

    // Java 8 Strategy pattern
    LOGGER.info("Green dragon spotted ahead!");
    dragonSlayer = new DragonSlayer(
        () -> LOGGER.info("With your Excalibur you severe the dragon's head!"));
    dragonSlayer.goToBattle();
    LOGGER.info("Red dragon emerges.");
    dragonSlayer.changeStrategy(() -> LOGGER.info(
        "You shoot the dragon with the magical crossbow and it falls dead on the ground!"));
    dragonSlayer.goToBattle();
    LOGGER.info("Black dragon lands before you.");
    dragonSlayer.changeStrategy(() -> LOGGER.info(
        "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"));
    dragonSlayer.goToBattle();
  }
}

前半部分使用的是之前定義的三個(gè)實(shí)現(xiàn)類,后半部分使用lambda表達(dá)式,其實(shí)相當(dāng)于:

dragonSlayer.changeStrategy(new DragonSlayingStrategy() {
    @Override
    public void execute(){/* 實(shí)現(xiàn)   */}
});

是不是很熟悉?對(duì)了,安卓里面的OnClick方法不就是這樣嗎?只不過貌似沒有預(yù)先定義的點(diǎn)擊事件。
也就是說,除了可以在預(yù)先定義好的策略里面選擇,還可以臨時(shí)創(chuàng)建一個(gè)新的匿名內(nèi)部類來實(shí)現(xiàn)接口。

總結(jié)

很多時(shí)候,代碼的修改和維護(hù)占的比重大于代碼的初創(chuàng),因此考慮代碼存在的改變可能而事先規(guī)劃好,某種程度上是能力到一定層次的體現(xiàn)。
策略模式假如使用得當(dāng),可以大大提升代碼的靈活性。應(yīng)該好好想一想,哪些代碼是在做一個(gè)本質(zhì)的不同方法實(shí)現(xiàn)?原來的實(shí)現(xiàn)是不是就是把所有方法堆在一起?可不可以抽象出來做一個(gè)接口,然后用策略模式來解決?
當(dāng)代碼簡(jiǎn)單的時(shí)候,可能看不出來特別的好處。比如上面的例子,我們當(dāng)然也可以直接在勇士類里面加入三種屠龍方法。那么好,第四種方法來了呢?再加上。假設(shè)現(xiàn)在要有一個(gè)法師類,除了只會(huì)用法術(shù)屠龍以外,別的都和勇士差不多,怎么辦?這時(shí)候弊端就體現(xiàn)出來了:因?yàn)榉◣煵粫?huì)近戰(zhàn),因此不能繼承勇士,代碼無法復(fù)用。
你可能會(huì)說:繼承啊沒關(guān)系,其他方法不用就行了。但這就會(huì)造成隱患,因?yàn)槟憔蜔o法阻止別人調(diào)用其他三種方法,就可能會(huì)造成錯(cuò)誤。這時(shí)代碼就顯得不優(yōu)雅。

參考

https://github.com/iluwatar/java-design-patterns/tree/master/strategy

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問題# 1.1 報(bào)價(jià)管理## 向客戶報(bào)價(jià),對(duì)于銷售部門的人來講,這是一個(gè)非常重大、非常復(fù)雜的問題,對(duì)不同的...
    七寸知架構(gòu)閱讀 5,249評(píng)論 9 62
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,545評(píng)論 19 139
  • 1 場(chǎng)景問題 1.1 報(bào)價(jià)管理 向客戶報(bào)價(jià),對(duì)于銷售部門的人來講,這是一個(gè)非常重大、非常復(fù)雜的問題,對(duì)不同的客戶要...
    4e70992f13e7閱讀 3,218評(píng)論 2 16
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,091評(píng)論 1 15
  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 8,120評(píng)論 2 17

友情鏈接更多精彩內(nèi)容