小白寫了一堆if-else,大神實在看不下去了,竟然用策略模式直接擺平了

這裡涉及到一個關鍵詞:策略模式,那麼到底什麼是策略模式呢?本文就來好好給大家講講策略模式,大家可以帶着如下幾個問題來閱讀本文:
 
1. 如何通過策略模式優化業務邏輯代碼(可以根據自己從事的工作思考)
2. 使用策略模式優化if-else,會不會帶來什麼副作用呢?
3. 實現策略模式是否有更好的方式呢?
 
1. 策略模式如何優化代碼解構
 
要會帶這個問題,需要先弄清楚策略模式的定義,首先來看策略模式的教科書定義:策略模式(Strategy Pattern):定義一系列算法,將每一個算法封裝起來,並讓它們可以相互替換。 策略模式讓算法獨立於使用它的客戶而變化,也稱為政策模式(Policy)。 策略模式是一種對象行為型模式。
 
這裡的算法其實就是業務邏輯,為了更形象,乾脆將其理解為一個函數。其實這個定義的基本意思就是說,根據策略選擇要執行的函數,而每一個策略都會有一個標識,可以稱為key。而當前策略要執行的函數,可以稱為value,其實就是使用key尋找value,然後執行vlaue的過程。也就是說,一個key對應一個value,從這層意思上理解,就是if-else要乾的事。

 

 

 
 那麼策略模式到底優化了什麼呢?
 
其實策略模式的核心思想與 if else如出一轍,根據不同的key動態的找到不同的業務邏輯(要執行的函數)。那它就只是如此嗎?
 
實際上,我們口中的策略模式其實就是在代碼結構上調整,用接口+實現類+分派邏輯來使代碼結構可維護性更好。
 
 一般教科書上講到接口與實現類就結束了,其他博客上會帶上提及分派邏輯。這裡就不啰嗦了。現在總結一下:即使用了策略模式,所有的業務邏輯一樣都少不了,改寫的仍然需要寫。到邏輯分派的時候,還是變相的if-else。而策略模式的優化點是抽象了出了接口,將業務邏輯封裝成一個一個的實現類,任意地替換。在複雜場景(業務邏輯較多)時比直接 使用if-else 更好維護。
 
2. 使用策略模式優化if-else,會不會帶來什麼副作用呢?
 
我估計肯定會有很多同學這麼想:我的業務邏輯就幾行,你給我整一大堆類定義?有必要這麼麻煩嗎?我看具體的業務邏輯還需要去不同的類中,簡單點不香嗎!
 
其實這裡的不滿也正是策略模式的缺點:
 
(1) 策略類會增多
(2) 業務邏輯分散到各個實現類中,而且沒有一個地方可以俯視整個業務邏輯
 
針對傳統策略模式的缺點,在這分享一個實現思路,這個思路已經幫我們團隊解決了多個複雜if else的業務場景,理解上比較容易,在技術上要使用到Java8的特性:Map與函數式接口。
 
廢話少說,直接看代碼:
 
先看兩個方法:
1. processResult() :使用if-else的方法
2. processPolicyResult():使用策略模式的方法。在該方法中事先在Map中定義好了「判斷條件」與「業務邏輯」的映射關係,具體看代碼吧!
 
/**
 * 策略模式演示類
 */
public class MyService {

    /**
     * 使用if-else的解決方案
     */
    public String processResult(String key) {
        if ("checkvalue1".equals(key)) {
            return "business logic1";   
        } else if ("checkvalue2".equals(key)) {
            return "business logic2";
        }else if ("checkvalue3".equals(key)) {
            return "business logic3";
        }else if ("checkvalue4".equals(key)) {
            return "business logic4";
        }else if ("checkvalue5".equals(key)) {
            return "business logic5";
        }else if ("checkvalue6".equals(key)) {
            return "business logic6";
        }else if ("checkvalue7".equals(key)) {
            return "business logic7";
        }else if ("checkvalue8".equals(key)) {
            return "business logic8";
        }else if ("checkvalue9".equals(key)) {
            return "business logic9";
        }
        return "error;
    }

    /**
     * 用於業務邏輯分派Map
     * Function為函數式接口,下面代碼中 Function<String, String> 的含義是接收一個String類型的變量,返回一個String類型的結果
     */
    private Map<String, Function<String, String>> myDispatcher = new HashMap<>();

    /**
     * 使用策略模式的方法
     */
  
    public void  policyInit() {
        myDispatcher.put("checkvalue1", key -> String.format("business logic1 for %s1", key));
        myDispatcher.put("checkvalue2", key -> String.format("business logic2 for %s2", key));
        myDispatcher.put("checkvalue3", key -> String.format("business logic3 for %s3", key));
        myDispatcher.put("checkvalue4", key -> String.format("business logic4 for %s4", key));
        myDispatcher.put("checkvalue5", key -> String.format("business logic5 for %s5", key));
        myDispatcher.put("checkvalue6", key -> String.format("business logic6 for %s6", key));
        myDispatcher.put("checkvalue7", key -> String.format("business logic7 for %s7", key));
        myDispatcher.put("checkvalue8", key -> String.format("business logic8 for %s8", key));
        myDispatcher.put("checkvalue9", key -> String.format("business logic9 for %s9", key));
    }

    public String processPolicyResult(String key) {
        //從邏輯分派Dispatcher中獲得業務邏輯代碼,result變量是一個lambda表達式
        Function<String, String> result = myDispatcher.get(key);
        if (result != null) {
            //執行這段表達式獲得String類型的結果
            return result.apply(key);
        }
        return "error";
    }
}

下面是調用代碼:

public class RunPolicy {
    private MyService myService;

    public String test(String key) {
        return myService.processPolicyResult(order);
    }
}

從這段代碼中可以看到很多好處,例如:

(1)在policyInit()方法中直觀地看到「判斷條件」與「業務邏輯」的映射關係;
(2)不需要單獨定義接口與實現類,直接使用現有的函數式接口即可;
 
3. 策略模式在真實場景中的應用
 
可能有的同學會說,我的條件判斷可能非常複雜,而前面的案例只有一個條件判斷(key),其實這就和數據庫中通過單個字段作為索引,還是使用複合索引(多個字段共同組成索引)的問題。我們也可以用複合條件來實現策略模式,上代碼:
/**
 * 策略模式類
 */
public class PolicyService {

    private Map<String, Function<String, String>> myDispatcherMulti = new HashMap<>();

    /**
     * 初始化 業務邏輯分派Map 其中value 存放的是 lambda表達式
     */
    @PostConstruct
    public void dispatcherMuitInit() {
        myDispatcherMulti.put("key_order1", key -> String.format("business logic1 for %s", key));
        myDispatcherMulti.put("key_order2_order3", key -> String.format("business logic2 for %s", key));
        myDispatcherMulti.put("key_order1_order2_order3", key -> String.format("business logic3 for %s", key));
    }

    public String processMuti(String key, int level) {
        //根據level獲取不同的key
        String dKey = getDispatcherKey(key, level);

        Function<String, String> result = myDispatcherMuti.get(dKey);
        if (result != null) {
            //執行這段表達式獲得String類型的結果
            return result.apply(key);
        }
        return "error";
    }

    /**
     * 根據level生成不同層次的key
     */
    private String getDispatcherKey(String key, int level) {
        StringBuilder k = new StringBuilder("key");
        for (int i = 1; i <= level; i++) {
            k.append("_" + order + i);
        }
        return k.toString();
    }
}

/**
 * 測試代碼
 */
public class  TestPolicyMulti {


    private PolicyService policyService;

    public String test(String key, int level) {
        return policyService.processMuti(key, level);
    }
}
 
在這段代碼中,key是滿足一定規則的複合條件,只要設計好key的生成規則就一切ok!
 
可能還會有很多同學問,我的業務邏輯有很多行,在dispatcherMuitInit()方法的Map中直接寫不會很長嗎?直接寫當然長了,我們可以抽象出一個服務類專門放業務邏輯,然後在定義中調用它就可以了,代碼如下:
 
/**
 * 專門放業務邏輯的服務類
 */
public class ServiceUnit {

    public String task1(String key) {
        return "業務邏輯1";
    }
    public String task2(String key) {
        return "業務邏輯2";
    }
     public String task3(String key) {
        return "業務邏輯3";
    }
     public String task4(String key) {
        return "業務邏輯4";
    }
}
/**
 * 使用策略模式的類
 */
public class PolicyService {
    private ServiceUnit serviceUnit;

    private Map<String, Function<String, String>> myDispatcher = new HashMap<>();

    /**
     * 初始化規則映射
     */
    public void dispatcherInit() {
        myDispatcher.put("key_order1", key -> serviceUnit.task1(key));
        myDispatcher.put("key_order1_order2", key -> serviceUnit.task2(key)));
        myDispatcher.put("key_order1_order2_order3", key -> serviceUnit.task3(key));
        myDispatcher.put("key_order1_order2_order3_order4", key -> serviceUnit.task4(key));
    }

    public String process(String key, int level) {
        // 根據level生成對應的key
        String dKey = getDispatcherKey(key, level);

        Function<String, String> result = myDispatcher.get(dKey);
        if (result != null) {
            //執行這段表達式獲得String類型的結果
            return result.apply(order);
        }
        return "error";
    }

    /**
     * 根據level生成對應的key
     */
    private String getDispatcherKey(String key, int level) {
        StringBuilder k = new StringBuilder("key");
        for (int i = 1; i <= level; i++) {
            k.append("_" + order + i);
        }
        return k.toString();
    }
}

總結:

 
1. 如何通過策略模式優化業務邏輯代碼(可以根據自己從事的工作思考)
抽象了出了接口,將業務邏輯封裝成的實現類,任意地替換。在複雜場景(業務邏輯較多)時比直接 使用if-else 更好維護。
 
2. 使用策略模式優化if-else,會不會帶來什麼副作用呢?
 
其實使用策略模式大多數時候會帶來很多好處,不過也會有一些不足的:
(1)策略類比較多;
(2)業務邏輯分散到各個實現類中,而且沒有一個地方可以俯覽整個業務邏輯;
3. 實現策略模式是否有更好的方式呢?
 
可以用函數函數式接口實現業務邏輯,這樣可以更直觀觀察策略和執行邏輯的關係。
 
喜歡我的文章,請掃描下面二維碼關注 『極客起源』 公眾號
 

 

 

也可以掃描下面二維碼加李寧老師的微信:unitymarvel