What
策略模式(Strategy Pattern)是一種行為設(shè)計模型。該模式定義一族算法類,將每個算法分別封裝起來,讓它們可以互相替換。策略模式可以使算法的變化獨(dú)立于使用它們的客戶端(這里的客戶端代指使用算法的代碼)。
Why
策略模式可以起到解耦的作用,其解耦的是策略的定義、創(chuàng)建、使用這三部分,可以避免復(fù)雜的分支判斷。具體優(yōu)點(diǎn)如下:
- 策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為,也可以靈活地增加新的算法或行為。
- 策略模式提供了管理相關(guān)的算法族的辦法。
- 策略模式提供了可以替換繼承關(guān)系的辦法。
- 使用策略模式可以避免使用多重條件轉(zhuǎn)移語句。
When
在以下情況下可以使用策略模式:
- 如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動態(tài)地讓一個對象在許多行為中選擇一種行為。
- 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。
- 如果一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J剑@些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)。
- 不希望客戶端知道復(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ī)則如下:
- 當(dāng)輸入數(shù)據(jù)長度小于10時,選擇插入排序;
- 當(dāng)輸入數(shù)據(jù)長度大于等于10并且小于20時,選擇歸并排序;
- 當(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]
代碼地址
寫在最后
如果你覺得我寫的文章幫到了你,歡迎點(diǎn)贊、評論、分享、贊賞哦,你們的鼓勵是我不斷創(chuàng)作的動力~