瞭解策略模式--通過詳細代碼與例子

瞭解策略模式–通過詳細代碼與例子

應用場景

完成一項任務,往往可以有多種策略可以實現,要想靈活選擇策略和添加新策略,可以使用策略模式。
比如從數組中查找某個值的任務,我們可以從頭到尾遍歷查找,可以從尾到頭遍歷查找,如果數組有序,還可以用二分法查找。不同的數組可以採用不同的策略來實現查找任務,我們還想讓數組的查找方式能設置與修改,這時候就可以用上策略模式,把不同的查找方法封裝成獨立的類,數組可以自行選擇使用哪種查找策略。

定義

策略模式定義了一系列策略,每一個策略都封裝起來,並讓他們可以相互轉換,使用策略的客戶可以任意選擇策略,策略的變化獨立於使用策略的客戶

例子

就用上面的數組查值爲例子,任務是從數組中查找某個值,策略有從頭到尾查找和從尾到頭查找,策略獨立於數組類,下面是UML類圖

在這裏插入圖片描述
NumberArray爲抽象類,內部屬性有protected的arr 和searchAlgorithm ,arr是Number數組,存放數據,searchAlgorithm是接口ISearchAlgorithm類型,後者定義了find方法用於從數據中查找某個數。實現了該接口的類有LeftSearchAlgorithm和RightSearchAlgorithm,分別實現了從頭到尾和從尾到頭查找數值的策略。NumberArray的子類有IntegerArray和DoubleArray,分別是整數數組和double小數數組,下面是代碼
ISearchAlgorithm.java

package priv.mxz.design_pattern.strategy_pattern;

interface ISearchAlgorithm {

    int find(Number[] arr, Number num);
}

class LeftSearchAlgorithm implements ISearchAlgorithm {

    @Override
    public int find(Number[] arr,Number num) {
        if (arr==null)
            return -1;
        for (int i=0; i<arr.length; i++){
            if (arr[i].equals(num))
                return i;
        }
        return -1;
    }

    @Override
    public String toString() {
        return "strategy_pattern.LeftSearchAlgorithm";
    }
}

class RightSearchAlgorithm implements ISearchAlgorithm {

    @Override
    public int find(Number[] arr, Number num) {
        if (arr==null)
            return -1;
        for (int i=arr.length-1; i>=0; i--){
            if (arr[i].equals(num))
                return i;
        }
        return -1;
    }

    @Override
    public String toString() {
        return "strategy_pattern.RightSearchAlgorithm";
    }
}


NumberArray.java

package priv.mxz.design_pattern.strategy_pattern;

import java.util.Arrays;

abstract class NumberArray {
    protected Number[] arr;
    protected ISearchAlgorithm searchAlgorithm;
    public NumberArray(Number[] arr){
        this.arr=arr;
    }

    int find(Number num){
        return searchAlgorithm.find(arr,num);
    }

    public void setSearchAlgorithm(ISearchAlgorithm searchAlgorithm){
        this.searchAlgorithm=searchAlgorithm;
    }

    @Override
    public String toString() {
        return "strategy_pattern.NumberArray{" +
                "arr=" + Arrays.toString(arr) +
                ", searchAlgorithm=" + searchAlgorithm +
                '}';
    }
}

class IntegerArray extends NumberArray{

    public IntegerArray(Integer[] arr,ISearchAlgorithm searchAlgorithm){
        super(arr);
        setSearchAlgorithm(searchAlgorithm);
    }


}

class DoubleArray extends NumberArray{
    public DoubleArray(Double[] arr,ISearchAlgorithm searchAlgorithm){
        super(arr);
        setSearchAlgorithm(searchAlgorithm);
    }

}


StrategyPattern.java

package priv.mxz.design_pattern.strategy_pattern;

public class StrategyPattern {

    public static void main(String[] args) {

        IntegerArray integerArray=new IntegerArray(
                new Integer[]{1,2,3,3,2,1},
                new LeftSearchAlgorithm());
        DoubleArray doubleArray=new DoubleArray(
                new Double[]{1.0,2.0,3.0,3.0,2.0,1.0},
                new LeftSearchAlgorithm());

        System.out.println("strategy_pattern.IntegerArray: "+integerArray);
        System.out.println("strategy_pattern.IntegerArray found 2 at index "+integerArray.find(2));

        System.out.println("Change integerArray search algorithm to right");
        integerArray.setSearchAlgorithm(new RightSearchAlgorithm());
        System.out.println("strategy_pattern.IntegerArray: "+integerArray);
        System.out.println("strategy_pattern.IntegerArray found 2 at index "+integerArray.find(2));

        System.out.println("strategy_pattern.DoubleArray: "+doubleArray);
        System.out.println("strategy_pattern.DoubleArray found 2 at index "+doubleArray.find(2.0));


    }
}

StrategyPattern類中有執行入口,main函數中可以看到,初始化時integerArray和doubleArray的查找策略都設置爲從頭到尾查找,然後打印integerArray的信息和調用find方法查找2的位置,結果爲1,即數組中第一個2的index。隨後我們通過integerArray.setSearchAlgorithm(new RightSearchAlgorithm());修改integerArray的查找策略爲從尾到頭,打印信息和調用find方法查找2的位置,結果爲4,即數組中第二個2的index。最後打印doubleArray的信息和調用find方法,發現doubleArray的查找策略不收integerArray的影響,不會因爲IntegerArray策略而改變。
運行輸出結果如下

strategy_pattern.IntegerArray: strategy_pattern.NumberArray{arr=[1, 2, 3, 3, 2, 1], searchAlgorithm=strategy_pattern.LeftSearchAlgorithm}
strategy_pattern.IntegerArray found 2 at index 1
Change integerArray search algorithm to right
strategy_pattern.IntegerArray: strategy_pattern.NumberArray{arr=[1, 2, 3, 3, 2, 1], searchAlgorithm=strategy_pattern.RightSearchAlgorithm}
strategy_pattern.IntegerArray found 2 at index 4
strategy_pattern.DoubleArray: strategy_pattern.NumberArray{arr=[1.0, 2.0, 3.0, 3.0, 2.0, 1.0], searchAlgorithm=strategy_pattern.LeftSearchAlgorithm}
strategy_pattern.DoubleArray found 2 at index 1

優缺點

在上述的例子中,如果不使用策略模式,採用硬編碼把find函數的實現寫入NumberArray或者它的子類中,則會有下面的問題

  1. 如果在NumberArray中實現find方法,那麼所有的子類的find方法都默認是同一個find策略,我們假設使用從頭到尾策略,除非子類重寫覆蓋find方法,那麼如果有多個子類要使用從尾到頭策略,這些子類都需要重寫一份從尾到頭策略,造成代碼的重複。比如DoubleArray自己實現了從尾到頭策略,此時若LongArray也要實現從尾到頭策略,就需要把DoubleArray中的複製過來做修改,這樣就造成了多份類似甚至相同的代碼,如果NumberArray不實現find方法,則每個子類都要寫自己的find方法,代碼的重複度更高。
  2. 如果子類要修改查找策略,只能修改源代碼,如果要實現在運行時改變查找策略,也要修改源代碼,添加if else判斷來確定查找策略,或者分別定義find和rfind。而是用策略模式只需要調用setSearchAlgorithm方法

所以,使用策略模式的優點如下:

  1. 策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統的基礎上選擇策略,也可以靈活地增加新策略。
  2. 策略模式提供了管理相關策略的辦法。(例子中的setSearchAlgorithm)
  3. 策略模式提供了可以替換繼承關係的辦法。
  4. 使用策略模式可以避免使用多重條件轉移語句。

策略模式的缺點如下:

  1. 客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。
  2. 策略模式將造成產生很多策略類,可以通過使用享元模式在一定程度上減少對象的數量。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章