設(shè)計模式系列篇(十四)——策略模式

What

策略模式(Strategy Pattern)是一種行為設(shè)計模型。該模式定義一族算法類,將每個算法分別封裝起來,讓它們可以互相替換。策略模式可以使算法的變化獨(dú)立于使用它們的客戶端(這里的客戶端代指使用算法的代碼)。

Why

策略模式可以起到解耦的作用,其解耦的是策略的定義、創(chuàng)建、使用這三部分,可以避免復(fù)雜的分支判斷。具體優(yōu)點(diǎn)如下:

  1. 策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為,也可以靈活地增加新的算法或行為。
  2. 策略模式提供了管理相關(guān)的算法族的辦法。
  3. 策略模式提供了可以替換繼承關(guān)系的辦法。
  4. 使用策略模式可以避免使用多重條件轉(zhuǎn)移語句。

When

在以下情況下可以使用策略模式:

  1. 如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動態(tài)地讓一個對象在許多行為中選擇一種行為。
  2. 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。
  3. 如果一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J剑@些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)。
  4. 不希望客戶端知道復(fù)雜的、與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu),在具體策略類中封裝算法和相關(guān)的數(shù)據(jù)結(jié)構(gòu),提高算法的保密性與安全性。

How

一個完整的策略模式就是由策略的定義、創(chuàng)建和使用三個部分組成的。策略類的定義比較簡單,包含一個策略接口和一組實(shí)現(xiàn)這個接口的策略類。策略的創(chuàng)建由工廠類來完成,封裝策略創(chuàng)建的細(xì)節(jié)。策略模式包含一組策略可選,客戶端代碼如何選擇使用哪個策略,有兩種確定方法:編譯時靜態(tài)確定和運(yùn)行時動態(tài)確定。其中,“運(yùn)行時動態(tài)確定”才是策略模式最典型的應(yīng)用場景。

今天,我們就以根據(jù)數(shù)據(jù)量自動選擇排序算法的實(shí)例來為大家介紹策略模式的實(shí)現(xiàn)方法。我們大家應(yīng)該都很熟悉排序算法有很多,冒泡、插入、選擇、希爾、歸并、快速排序等等等,我們現(xiàn)在的需求是對于輸入數(shù)據(jù)的數(shù)據(jù)量來動態(tài)選擇排序算法,規(guī)則如下:

  1. 當(dāng)輸入數(shù)據(jù)長度小于10時,選擇插入排序;
  2. 當(dāng)輸入數(shù)據(jù)長度大于等于10并且小于20時,選擇歸并排序;
  3. 當(dāng)輸入數(shù)據(jù)長度大于等于20時,選擇快速排序。

接下來,我們就以策略的定義、創(chuàng)建和使用來分別介紹策略模式的實(shí)現(xiàn)方法。
首先是策略的定義,代碼如下:

// 排序算法接口
public interface SortAlgorithm {
    int[] sort(int[] list);
}

// 插入排序算法
public class InsertSortAlgo implements SortAlgorithm {
    @Override
    public int[] sort(int[] list) {
        if (list == null || list.length < 2) {
            return list;
        }

        for (int i = 0; i < list.length; i++) {
            int value = list[i];
            int j = i;
            for (; j > 0; j--) {
                // 移動數(shù)據(jù)
                if (value < list[j - 1]) {
                    list[j] = list[j - 1];
                } else {
                    break;
                }
            }
            // 插入數(shù)據(jù)
            list[j] = value;
        }
        return list;
    }
}

// 歸并排序算法
public class MergeSortAlgo implements SortAlgorithm {
    @Override
    public int[] sort(int[] list) {
        if (list == null || list.length < 2) {
            return list;
        }

        if (list.length == 2) {
            if (list[0] > list[1]) {
                int tmp = list[0];
                list[0] = list[1];
                list[1] = tmp;
            }
            return list;
        }
        int left = 0;
        int right = list.length;
        int mid = (left + right) / 2;
        int[] leftA = new int[mid];
        int[] rightA = new int[right - mid];
        int i = 0;
        int j = mid;
        while (i < mid) {
            leftA[i] = list[i++];
        }
        while (j < right) {
            rightA[j - mid] = list[j++];
        }
        leftA = sort(leftA);
        rightA = sort(rightA);
        return merge(leftA, rightA);
    }

    private static int[] merge(int[] list, int[] B) {
        int lenA = list.length;
        int lenB = B.length;
        int[] C = new int[lenA + lenB];
        int i = 0;
        int j = 0;
        int m = 0;
        while (i < lenA && j < lenB) {
            if (list[i] <= B[j]) {
                C[m++] = list[i++];
            } else {
                C[m++] = B[j++];
            }
        }
        while (i < lenA) {
            C[m++] = list[i++];
        }

        while (j < lenB) {
            C[m++] = B[j++];
        }
        return C;
    }
}

// 快速排序算法
public class QuickSortAlgo implements SortAlgorithm {

    @Override
    public int[] sort(int[] list) {
        if (list == null || list.length < 2) {
            return list;
        }
        partition(list, 0, list.length - 1);
        return list;

    }

    private void partition(int[] list, int left, int right) {
        if (left > right) {
            return;
        }
        int i = left;
        int j = right;
        int base = list[left];
        while (i < j) {
            while (list[j] >= base && i < j) {
                j--;
            }

            if (i < j) {
                list[i] = list[j];
            }

            while (list[i] <= base && i < j) {
                i++;
            }
            if (i < j) {
                list[j] = list[i];
            }
        }
        list[i] = base;

        partition(list, left, i - 1);
        partition(list, i + 1, right);
    }
}

接下來,是策略的創(chuàng)建。一般采用工廠模式進(jìn)行生成。

public class SortAlgoFactory {
    private static List<RangeAlgo> algorithms = new ArrayList<>();

    static {
        algorithms.add(new RangeAlgo(0, 10, new InsertSortAlgo()));
        algorithms.add(new RangeAlgo(10, 20, new MergeSortAlgo()));
        algorithms.add(new RangeAlgo(20, Integer.MAX_VALUE, new QuickSortAlgo()));
    }

    public void addAlgorithms(RangeAlgo rangeAlgo) {
        algorithms.add(rangeAlgo);
    }

    public static SortAlgorithm newSortAlgorithm(Integer num) {
        for (RangeAlgo algo : algorithms) {
            if (algo.inRange(num)) {
                System.out.println(String.format("use sort algorithm %s", algo.getAlgorithm().getClass()));
                return algo.getAlgorithm();
            }
        }
        throw new IllegalStateException("invalid num");
    }

    private static class RangeAlgo {
        private Integer min;
        private Integer max;

        private SortAlgorithm algorithm;

        public SortAlgorithm getAlgorithm() {
            return algorithm;
        }

        public RangeAlgo(Integer min, Integer max, SortAlgorithm algorithm) {
            this.min = min;
            this.max = max;
            this.algorithm = algorithm;
        }

        public boolean inRange(Integer num) {
            return num >= this.min && num < this.max;
        }
    }
}

如果你想增加更多的排序算法,只需要實(shí)現(xiàn)新的排序策略算法,然后調(diào)用addAlgorithms方法更新algorithms列表就可以了,所以這很符合開閉原則。

最后,是策略的使用。測試代碼如下:

public class TestMain {
    public static void main(String[] args) {
        int[] A = new int[]{3, 1, 2, 4, 6};
        SortAlgorithm sortAlgorithm1 = SortAlgoFactory.newSortAlgorithm(A.length);
        A = sortAlgorithm1.sort(A);
        System.out.println(Arrays.toString(A));

        int[] B = new int[]{3, 1, 2, 4, 6, 2, 4, 10, 9, 20, 39};
        SortAlgorithm sortAlgorithm2 = SortAlgoFactory.newSortAlgorithm(B.length);
        B = sortAlgorithm2.sort(B);
        System.out.println(Arrays.toString(B));

        int[] C = new int[]{3, 1, 2, 4, 6, 2, 4, 10, 9, 20, 39, 3, 1, 2, 4, 6, 2, 4, 10,
                9, 20, 39, 3, 1, 2, 4, 6, 2, 4, 10, 9, 20, 39};
        SortAlgorithm sortAlgorithm3 = SortAlgoFactory.newSortAlgorithm(C.length);
        C = sortAlgorithm3.sort(C);
        System.out.println(Arrays.toString(C));
    }
}

輸出如下:

use sort algorithm class strategy.InsertSortAlgo
[1, 2, 3, 4, 6]
use sort algorithm class strategy.MergeSortAlgo
[1, 2, 2, 3, 4, 4, 6, 9, 10, 20, 39]
use sort algorithm class strategy.QuickSortAlgo
[1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 6, 6, 6, 9, 9, 9, 10, 10, 10, 20, 20, 20, 39, 39, 39]

代碼地址

i-learning

寫在最后

如果你覺得我寫的文章幫到了你,歡迎點(diǎn)贊、評論、分享、贊賞哦,你們的鼓勵是我不斷創(chuàng)作的動力~

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

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