策略模式在java集合中的TreeSet和TreeMap中獲得了很好的應用,咱們能夠實現Comparator接口實現Compareto()方法來定義本身的排序規則,而後經過TreeSet,TreeMap構造方法傳入實現該接口的實例,map中的順序就會是咱們自定義的順序。咱們能夠徹底定義本身的規則,用之極爲方便。那麼,什麼是策略模式呢?java
策略模式定義:定義一組算法,將每一個算法都封裝起來,而且使它們之間能夠轉換。策略模式使這些算法在客戶端調用時可以互不影響的變化。算法
策略模式組成:編程
1.抽象的策略角色:策略類,一般由一個抽象類或者接口實現。數組
2.具體的策略角色:包裝了相關的算法和行爲。數據結構
3.環境角色:持有一個策略類的引用,以便最終給客戶端調用。ide
策略模式的設計原則:ui
1. 封裝變化this
2.使用接口編程spa
策略模式的實現:設計
1.將每一組算法封裝到具備共同接口的獨立類中,這樣不一樣的策略能夠相互替換
2.算法能夠在不影響客戶端的狀況下發生變化,把行爲和環境分開。
3.環境類負責維持和查詢行爲類,各類算法在具體策略中提供
看文字看蒙了,舉一個具體的例子吧,舉什麼例子呢?想到咱們數據結構中的各類排序,什麼冒泡排序,快速排序,堆排序...,他們完成的功能都是同樣的,只是排序算法不一樣而已。咱們就能夠當作是排序的不一樣策略。
首先定義咱們的抽象策略:
/** * 抽象的策略類 * @author * */
public interface Strategy { public void sort(int[] array); }
定義具體的策略,這裏實現了三種策略,分別是快速排序,堆排序,歸併排序。
快速排序策略代碼:
public class QuickSort implements Strategy{ @Override public void sort(int[] array) { qpSort(array,0,array.length - 1); } private void qpSort(int array[], int low, int high) { int pos = qkPass(array, low, high); //產生中間第一個肯定的數
if (low < high) { qpSort(array, low, pos - 1);//左邊
qpSort(array, pos + 1, high);//右邊
} } private int qkPass(int a[], int low, int high) { int x = a[low]; while (low < high) { while (low < high && a[high] > x) { high--; } if (low < high) { a[low] = a[high]; low++; } while (low < high && a[low] < x) { low++; } if (low < high) { a[high] = a[low]; high--; } } a[low] = x; return low; } }
堆排序策略代碼:
public class HeapSort implements Strategy { @Override public void sort(int[] array) { heapSort(array,array.length); } private void heapSort(int a[], int length) { crtHeap(a, length); // 建立大根堆
for (int i = length - 1; i > 0; i--) { int b = a[0]; a[0] = a[i]; // 堆頂跟堆尾互換
a[i] = b; sift(a, 0, i - 1); // 使得r[0...i-1]變成堆
} } /** * 創建初堆 * * @param a * 爲帶排序的數組 * @param length * 爲數組長度 */
private void crtHeap(int a[], int length) { int n = length - 1; // 建出堆,從第 n/2 個記錄開始進行堆篩選
for (int i = n / 2; i >= 0; i--) { sift(a, i, n); } } /** * * @param r * 數組 * @param k * 表示以r[k]爲根的徹底二叉樹,調整r[k],使得r[k...m]知足大根堆的性質 * @param m * 堆尾元素下標 */
private void sift(int r[], int k, int m) { int temp = r[k]; // 暫存根記錄
int i = k; // 記錄初始根的下標
int j = i * 2 + 1; // 根的左孩子,由於下標從0開始
while (j <= m) { // 若是存在右子樹,且右子樹根的關鍵字大,則沿右分支篩選,不然沿左分支篩選。由於目的是找一個最大的元素
if (j < m && r[j] < r[j + 1]) { j = j + 1; } if (temp >= r[j]) { break; // 結束篩選
} else { r[i] = r[j]; // 上移
i = j; // 重新定義根下標
j = j * 2 + 1;// 繼續篩選
} } r[i] = temp; // 將r[k]移動到適當的位置
} }
歸併排序策略代碼:
public class MergeSort implements Strategy{ @Override public void sort(int[] array) { int[] temp = new int[array.length]; mSort(array,0,array.length - 1, temp); } /** * * @param r1 等待排序數組 * @param low * @param high * @param r3 將r1排序後的結果放在r3中 r1[low...high],r3[low...high] */
private void mSort(int r1[],int low,int high, int r3[]){ if(low < high) { int mid = (low + high) /2; //歸併排序中 拆,合要一塊兒進行
mSort(r1,low,mid,r3); mSort(r1,mid+1,high,r3); merge(r1,low, mid, high, r3); } } private void merge(int r1[],int low, int mid, int high, int r2[]){ int i = low; int j = mid + 1; int k = 0; while(i <=mid && j <= high){ if(r1[i] <= r1[j]){ r2[k] = r1[i]; i++; } else{ r2[k] = r1[j]; j++; } k++; } while(i <= mid){ r2[k] = r1[i]; i++; k++; } while(j <= high){ r2[k] = r1[j]; j++; k++; } for(int m = 0; m < k;m++){ r1[low + m] = r2[m]; } } }
定義環境類:
public class Environment { private Strategy strategy; //策略類的引用
public Environment(Strategy strategy){ this.strategy = strategy; } //用於設置不一樣的策略
public void setStrategy(Strategy strategy){ this.strategy = strategy; } //實現排序的功能
public void sort(int array[]){ strategy.sort(array); } }
最後定義客戶端類:
public class Client { public static void main(String[] args) { int[] array1 = new int[]{48,62,35,77,55,14,35,98}; int[] array2 = new int[]{48,62,35,77,55,14,35,98}; int[] array3 = new int[]{48,62,35,77,55,14,35,98}; //使用堆排序策略
Environment env = new Environment(new HeapSort()); env.sort(array1); System.out.println("使用堆排序array1:"); print(array1); //使用快速排序策略
env.setStrategy(new QuickSort()); env.sort(array2); System.out.println("使用快速排序array2:"); print(array2); //使用歸併排序策略
env.setStrategy(new MergeSort()); env.sort(array3); System.out.println("使用歸併排序array3:"); print(array3); } static void print(int[] array){ for(int i = 0; i < array.length; i++){ System.out.print(array[i] + "\t"); } System.out.println(); System.out.println("---------------------------------"); } }
運行結果以下所示:
咱們使用不一樣的排序策略分別對三個數組排序的功能已經實現了,程序很是靈活,咱們想用什麼策略來實現排序能夠本身決定(經過environment.serStrategy(XXX)方法)。
程序中Environment類根本不知道也不用管排序究竟是怎麼實現的,它只是持有一個具體策略類的引用,而後調用具體策略類的方法。
策略模式用着很方便,可是自己也有缺點:
1.客戶端必須知道全部的策略類,並自行決定使用哪一種策略類。
2.形成不少的策略類,每個不一樣的策略都須要一個策略類來實現。
對於第一點很明顯,咱們客戶端(client類)在使用策略時必須知道具體的策略類是什麼,environment.serStrategy(XXX)方法也須要知道。
對於第二點,定義了3中策略,就有3個策略類,若是策略不少的話,類也會不少。
參考資料:聖思園教學視頻