首先我們需要知道策略模式與狀態模式是如此的相似,就猶如一對雙胞胎一樣。只不過狀態模式是通過改變對象內部的狀態來幫助對象控制自己的行為,而策略模式則是圍繞可以互換的算法來創建成功業務的。兩者都可用於解決同一個問題:帶有大量的if..else…等條件判斷語句來進行選擇的(小弟初學,不知這樣理解是否有誤??有誤望指出…)。
在我們的生活中我們可以通過很多種不同的方式來完成一件事情,這里的每一種方式都可以稱作為一種策略。我們可以根據環境、條件等因素的不同選擇不同的策略來完成這件事情。比如說出去旅游,我們可以選擇坐火車、坐飛機、坐大巴、騎自行車,甚至徒步等等方式。如果想舒適快速我們可以選擇飛機,節約錢我們可以選擇火車和大巴,離家近的我們可以騎自行車,每一種方式都可以到達目的地。但是總有一種方式是當前最適合你的。你會選擇哪種出行方式呢?
在軟件開發過程中是一樣的,我們可能有很多種方法可以實現某一個功能,但是我們需要一種簡單、高效的途徑可以使得系統能夠靈活的解決問題。這就是策略模式的設計動機。
一、模式定義
在前面中對策略模式應該有了一個最初步的認識。那么什么是策略模式呢?所謂策略模式就是定義了算法族,分別封裝起來,讓他們之前可以互相轉換,此模式然該算法的變化獨立於使用算法的客戶。
在軟件系統中有很多種方法可以實現同一個功能,比如排序算法它有冒泡排序、選擇排序、快速排序、插入排序等等。這里我們有一種硬編碼方法,就是講所以的排序算法全部寫在一個類中,每一種算法的具體實現對應着一個方法,然后寫一個總方法通過if…else…來判斷選擇具體的排序算法,但是這樣做存在幾個問題。
第一:如果需要增加新的算法,則需要修改源代碼。
第二:如果更新了排序算法,那么需要在客戶端也需要修改代碼,麻煩。
第三:充斥着大量的if…else…語句,代碼維護比較困難。
所以為了解決這些問題,我們可以定義一些獨立的類來封裝不同的算法,每一個獨立的類對應着一個具體的算法實現,在這里我們就將這里每一個獨立的類稱之為一個策略。
二、模式結構
下圖為策略模式的結構:
這個UML圖與狀態模式的幾乎一模一樣,狀態模式是通過改變對象內部的狀態來幫助對象控制自己的行為,我們可以這樣理解狀態模式其實是對狀態進行封裝,它是將動作動作的實現和責任進行分割,把動作委托到代表當前狀態的對象。而策略模式是對算法進行封裝,把算法的責任和算法本身進行分割開來,同時委派給不同的對象進行管理。策略模式是將一個系列的算法封裝到一系列的策略里面。
其實對於算法的選擇,策略模式並不關心,它只是對算法進行封裝,至於算法什么時候什么地方使用什么算法都是客戶所決定的,這樣就提高了系統的靈活性,但同時也增加了客戶的負擔,因為客戶需要清楚知道選擇什么樣的算法對自己最有利,這就需要客戶對每一個算法都清楚知道他們的區別。
三、模式實現
我們都知道排序算法有很多種,但是什么時候選擇冒泡排序,什么時候選擇選擇排序,什么時候選擇插入排序,所以這里用排序算法來演示策略模式的實現。
首先我們先來看看在沒有使用策略模式的情況。
public void selectSort(String type){
if("type1".equals(type)){
//選擇快速排序
}
else if("type2".equals(type)){
//選擇插入排序
}
else if("type3".equals(type)){
//選擇冒泡排序
}
else if("type4".equals(type)){
//選擇選擇排序
}
......
}
對於這樣的代碼實現,除了代碼中充斥着大量的if…else if…else,導致程序可維護性很差,而且系統的可擴展性不好,如果某個排序模塊進行更改了,有可能需要修改源代碼。所以在對於這樣的情景是非常合適使用策略模式的。
那么如何使用策略模式呢?首先我們需要定義一個接口,該接口提供排序算法,然后定義想要的排序算法,實現給接口即可。如下:
首先是Sort接口,該接口定義了排序算法,所有的排序算法都應該實現該接口。
public interface Sort{ public abstract int[] sort(int arr[]); }
然后是三個具體的排序算法,他們實現Sort接口。
冒泡排序:BubbleSort.java
public class BubbleSort implements Sort{ public int[] sort(int arr[]){ int len=arr.length; for(int i=0;i<len;i++){ for(int j=i+1;j<len;j++){ int temp; if(arr[i]>arr[j]){ temp=arr[j]; arr[j]=arr[i]; arr[i]=temp; } } } System.out.println("冒泡排序"); return arr; } }
插入排序:InsertionSort.java
public class InsertionSort implements Sort { public int[] sort(int arr[]) { int len = arr.length; for (int i = 1; i < len; i++) { int j; int temp = arr[i]; for (j = i; j > 0; j--) { if (arr[j - 1] > temp) { arr[j] = arr[j - 1]; } else break; } arr[j] = temp; } System.out.println("插入排序"); return arr; } }
選擇排序:SelectSort.java
public class SelectionSort implements Sort { public int[] sort(int arr[]) { int len = arr.length; int temp; for (int i = 0; i < len; i++) { temp = arr[i]; int j; int samllestLocation = i; for (j = i + 1; j < len; j++) { if (arr[j] < temp) { temp = arr[j]; samllestLocation = j; } } arr[samllestLocation] = arr[i]; arr[i] = temp; } System.out.println("ѡ������"); return arr; } }
最后就是測試類客戶端了Client.java
public class ArrayHandler { private Sort sortObj; public int[] sort(int arr[]) { sortObj.sort(arr); return arr; } public void setSortObj(Sort sortObj) { this.sortObj = sortObj; } }
public class Client { public static void main(String args[]) { int arr[]={1,4,6,2,5,3,7,10,9}; int result[]; ArrayHandler ah=new ArrayHandler(); Sort sort = new SelectionSort(); //使用選擇排序 ah.setSortObj(sort); //設置具體策略 result=ah.sort(arr); for(int i=0;i<result.length;i++) { System.out.print(result[i] + ","); } } }
運行結果
選擇排序
1,2,3,4,5,6,7,9,10,
四、模式優缺點
優點
1、策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統的基礎上選擇算法或行為,也可以靈活地增加新的算法或行為。
2、策略模式提供了可以替換繼承關系的辦法。
3、使用策略模式可以避免使用多重條件轉移語句。
缺點
1、客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。
2、策略模式將造成產生很多策略類,
五、使用場景
1、如果在一個系統里面有許多類,它們之間的區別僅在於它們的行為,那么使用策略模式可以動態地讓一個對象在許多行為中選擇一種行為。
2、一個系統需要動態地在幾種算法中選擇一種。
3、如果一個對象有很多的行為,如果不用恰當的模式,這些行為就只好使用多重的條件選擇語句來實現。