策略模式--避免冗長(zhǎng)的if/else

算是讀書筆記吧

極客時(shí)間--設(shè)計(jì)模式之美


什么是策略模式

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
定義一族算法類,將每個(gè)算法分別封裝起來(lái),讓它們可以互相替換。策略模式可以使算法的變化獨(dú)立于使用它們的客戶端(這里的客戶端代指使用算法的代碼)

策略模式對(duì)策略的定義、創(chuàng)建、使用三部分進(jìn)行了解耦


策略模式的作用

策略模式解決的問(wèn)題

策略模式表面上看是為了避免 if-else 分支判斷邏輯,但更深層次上,還是為了解耦以及控制代碼復(fù)雜度

比如在出門旅游時(shí):路線、交通工具的類型、天數(shù)、艙位等級(jí)、餐飲、住宿等等。
每個(gè)節(jié)點(diǎn)在執(zhí)行時(shí),都需要根據(jù)預(yù)算Type進(jìn)行不同的操作,從而引起大量的if-else判斷。增加一個(gè)策略,修改一個(gè)策略,都有可能牽一發(fā)而動(dòng)全身。需要對(duì)所有狀態(tài)進(jìn)行回測(cè)。

public void departure () {
  Int money = 100;
  String destination = getDestination(money); //獲取目的地
  String vehicles = getVehicles(money); //獲取交通工具
  天數(shù)、艙位等級(jí)、餐飲、住宿等等.....
}

private String getDestination (Int money) {
  if (num < 1000) {
    return "1000塊能去得起的地方";
  } else if (num < 5000) {
    return "5000塊能去得起的地方";
  } else if (num < 10000) {
    return "10000塊能去得起的地方";
  } else if (num < 20000) {
    return "20000塊能去得起的地方";
  } else if (num < 40000) {
    return "40000塊能去得起的地方";
  } else if (num < 80000) {
    return "80000塊能去得起的地方";
  } else  ...
}

private String getVehicles (Int money) {
  if (num < 1000) {
    return "騎車";
  } else if (num < 5000) {
    return "火車";
  } else if (num < 10000) {
    return "火車";
  } else if (num < 20000) {
    return "飛機(jī)";
  } else if (num < 40000) {
    return "飛機(jī)";
  } else if (num < 80000) {
    return "飛機(jī)";
  } else  ...
}

...后面還有一大堆相關(guān)的方法需要判斷

整個(gè)業(yè)務(wù)如圖所示,所有的判斷都耦合在業(yè)務(wù)流程內(nèi)部,牽一發(fā)而動(dòng)全身

使用策略模式

我們可以將某一條件(Type)下的邏輯,聚合封裝到具體的策略類中

public class departureStrategy1000 implements Strategy {  //1000塊的旅行策略
  @Override
  public void  getDestination() {
    return "1000塊能去得起的地方";
  }
  public void  getVehicles() {
    return "騎車";
  }
  天數(shù)、艙位等級(jí)、餐飲、住宿等等.....
}

使用策略類后如圖所示,每個(gè)的情況被封裝聚合到單個(gè)策略類中,相互隔離


所以策略模式的作用主要體現(xiàn)在:

  1. 解耦策略的定義、創(chuàng)建和使用
    控制代碼的復(fù)雜度,讓每個(gè)部分都不至于過(guò)于復(fù)雜、代碼量過(guò)多。
  2. 讓復(fù)雜框架滿足開閉原則
    添加或者修改新策略的時(shí)候,最小化、集中化代碼改動(dòng),減少引入 bug 的風(fēng)險(xiǎn)。

策略的定義

策略的定義包含一個(gè)策略接口和一組實(shí)現(xiàn)這個(gè)接口的策略類。
利用基于接口而非實(shí)現(xiàn)編程的方式,對(duì)具體策略進(jìn)行解耦。

如下,策略類ConcreteStrategyA、ConcreteStrategyB在策略接口algorithmInterface的使用上,可以隨意替換。

public interface Strategy {  //定義策略接口
  void algorithmInterface();
}

public class ConcreteStrategyA implements Strategy {  //實(shí)現(xiàn)策略接口的策略類A
  @Override
  public void  algorithmInterface() {
    //具體的算法...
  }
}

public class ConcreteStrategyB implements Strategy { //實(shí)現(xiàn)策略接口的策略類B
  @Override
  public void  algorithmInterface() {
    //具體的算法...
  }
}

策略的創(chuàng)建

通常會(huì)通過(guò)類型(type)來(lái)判斷創(chuàng)建哪個(gè)策略來(lái)使用。

這里,有兩種創(chuàng)建方式

if-else創(chuàng)建

適用有狀態(tài)的策略類,每次創(chuàng)建一個(gè)新的策略類給業(yè)務(wù)方使用


public class StrategyFactory {
  public static Strategy getStrategy(String type) {
    if (type == null || type.isEmpty()) {
      throw new IllegalArgumentException("type should not be empty.");
    }

    if (type.equals("A")) {
      return new ConcreteStrategyA();
    } else if (type.equals("B")) {
      return new ConcreteStrategyB();
    }

    return null;
  }
}

通過(guò)工廠模式里的Map進(jìn)行創(chuàng)建

適用于無(wú)狀態(tài)的策略類創(chuàng)建,大家共用一個(gè)策略類即可

public class StrategyFactory {
  private static final Map<String, Strategy> strategies = new HashMap<>();

  static {
    strategies.put("A", new ConcreteStrategyA());
    strategies.put("B", new ConcreteStrategyB());
  }

  public static Strategy getStrategy(String type) {
    if (type == null || type.isEmpty()) {
      throw new IllegalArgumentException("type should not be empty.");
    }
    return strategies.get(type);
  }
}

本質(zhì)上點(diǎn)講,是借助“查表法”,根據(jù) type 查表替代根據(jù) type 分支判斷。

有狀態(tài)的策略類如何用Map進(jìn)行創(chuàng)建

可以利用閉包的特性,將創(chuàng)建的邏輯封裝進(jìn)callback中,然后將callback存進(jìn)Map


策略類的使用

如果使用工廠方法創(chuàng)建策略類,其實(shí)就和工廠方法相同。
只不過(guò)我們從工廠取出來(lái)的不再是一個(gè)某一個(gè)具體類的子類簇。
而是一個(gè)實(shí)現(xiàn)了策略接口的類簇。

// 運(yùn)行時(shí)動(dòng)態(tài)確定,根據(jù)配置文件的配置決定使用哪種策略
public class Application {
  public static void main(String[] args) throws Exception {
    Strategy strategy = null;
    StrategyFactory factory = new StrategyFactory(); 
    strategy = factory.getStrategy("A"); //獲取策略類
    strategy.algorithmInterface(); //調(diào)用策略接口
    //...
  }
}

是不是要抹殺所有的if/else

如果 if-else 分支判斷不復(fù)雜、代碼不多,這并沒(méi)有任何問(wèn)題,畢竟 if-else 分支判斷幾乎是所有編程語(yǔ)言都會(huì)提供的語(yǔ)法,存在即有理由。遵循 KISS 原則,怎么簡(jiǎn)單怎么來(lái),就是最好的設(shè)計(jì)。非得用策略模式,搞出 n 多類,反倒是一種過(guò)度設(shè)計(jì)

最后編輯于
?著作權(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)容

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