策略模式
策略模式的用意是針對一組算法,將每一個算法封裝到具有共同接口的獨立類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發生變化。
策略模式的結構
策略模式是對算法的包裝,是把使用算法的責任和算法本身分開。策略模式通常是把一系列的算法包裝到一系列的策略類里面,作為一個抽象策略類的子類。
策略模式涉及到三個角色:
1、環境角色
持有一個策略Strategy的引用
2、抽象策略角色
這是一個抽象角色,通常由一個接口或抽象類實現,此角色給出所有具體策略類所需的接口
3、具體策略角色
包裝了相關算法或行為
策略模式實際應用場景-容錯恢復機制
容錯恢復機制是應用程序開發中非常常見的功能。那么什么是容錯恢復呢?簡單點說就是:程序運行的時候,正常情況下應該按照某種方式來做,如果按照某種方式來做發生錯誤的話,系統並不會崩潰,也不會就此不能繼續向下運行了,而是有容忍出錯的能力,不但能容忍程序運行出現錯誤,還提供出現錯誤后的備用方案,也就是恢復機制,來代替正常執行的功能,使程序繼續向下運行。
舉個實際點的例子吧,比如在一個系統中,所有對系統的操作都要有日志記錄,而且這個日志還需要有管理界面,這種情況下通常會把日志記錄在數據庫里面,方便后續的管理,但是在記錄日志到數據庫的時候,可能會發生錯誤,比如暫時連不上數據庫了,那就先記錄在文件里面,然后在合適的時候把文件中的記錄再轉錄到數據庫中。
對於這樣的功能的設計,就可以采用策略模式,把日志記錄到數據庫和日志記錄到文件當作兩種記錄日志的策略,然后在運行期間根據需要進行動態的切換。
示例
1.定義日志策略接口
1 public interface LogStrategy { 2 3 public void log(String msg); 4 5 }
2.實現日志策略接口
1)記錄到數據庫
1 public class DbLog implements LogStrategy{ 2 3 public void log(String msg) { 4 5 System.out.println("現在把 '"+msg+"' 記錄到數據庫中"); 6 7 } 8 9 }
2)記錄到文件
1 public class FileLog implements LogStrategy{ 2 3 public void log(String msg) { 4 5 System.out.println("現在把 '"+msg+"' 記錄到文件中"); 6 7 } 8 9 }
3)接下來定義使用這些策略的上下文,注意這次是在上下文里面實現具體策略算法的選擇,所以不需要客戶端來指定具體的策略算法了,示例代碼如下:
1 public class LogContext { 2 3 public void log(String msg) { 4 5 LogStrategy strategy = new DbLog(); 6 try { 7 strategy .log(msg); 8 } catch(Exception e) { 9 // 出錯,記錄到文件 10 strategy = new FileLog(); 11 strategy.log(msg); 12 } 13 } 14 }
4.小結
通過上面的示例,會看到策略模式的一種簡單應用,也順便了解一下基本的容錯恢復機制的設計和實現。在實際的應用中,需要設計容錯恢復的系統一般要求都比較高,應用也會比較復雜,但是基本的思路是差不多的。
Java中的策略接口-Comparator接口
比方說Collections里面有一個sort方法,因為集合里面的元素有可能是復合對象,復合對象並不像基本數據類型,可以根據大小排序,復合對象怎么排序呢?基於這個問題考慮,Java要求如果定義的復合對象要有排序的功能,就自行實現Comparable接口或Comparator接口,看一下sort帶Comparator的重載方法:
1 public static <T> void sort(List<T> list, Comparator<? super T> c) { 2 Object[] a = list.toArray(); 3 Arrays.sort(a, (Comparator)c); 4 ListIterator i = list.listIterator(); 5 for (int j=0; j<a.length; j++) { 6 i.next(); 7 i.set(a[j]); 8 } 9 }
看一下第3行:
1 public static <T> void sort(T[] a, Comparator<? super T> c) { 2 T[] aux = (T[])a.clone(); 3 if (c==null) 4 mergeSort(aux, a, 0, a.length, 0); 5 else 6 mergeSort(aux, a, 0, a.length, 0, c); 7 }
再看一下第6行:
1 private static void mergeSort(Object[] src, 2 Object[] dest, 3 int low, int high, int off, 4 Comparator c) { 5 int length = high - low; 6 7 // Insertion sort on smallest arrays 8 if (length < INSERTIONSORT_THRESHOLD) { 9 for (int i=low; i<high; i++) 10 for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--) 11 swap(dest, j, j-1); 12 return; 13 } 14 15 // Recursively sort halves of dest into src 16 int destLow = low; 17 int destHigh = high; 18 low += off; 19 high += off; 20 int mid = (low + high) >>> 1; 21 mergeSort(dest, src, low, mid, -off, c); 22 mergeSort(dest, src, mid, high, -off, c); 23 24 // If list is already sorted, just copy from src to dest. This is an 25 // optimization that results in faster sorts for nearly ordered lists. 26 if (c.compare(src[mid-1], src[mid]) <= 0) { 27 System.arraycopy(src, low, dest, destLow, length); 28 return; 29 } 30 31 // Merge sorted halves (now in src) into dest 32 for(int i = destLow, p = low, q = mid; i < destHigh; i++) { 33 if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0) 34 dest[i] = src[p++]; 35 else 36 dest[i] = src[q++]; 37 } 38 }
第10行,根據Comparator接口實現類的compare方法的返回結果決定是否要swap(交換)。
這就是策略模式,我們可以給Collections的sort方法傳入不同的Comparator的實現類作為不同的比較策略。不同的比較策略,對同一個集合,可能會產生不同的排序結果。
策略模式優缺點
優點
1、避免了多重條件if...else if...else語句,多重條件語句並不容易維護
2、策略模式提供了管理相關算法簇的辦法,恰當使用繼承可以把公共代碼移到父類,從而避免了代碼重復
缺點
1、客戶端必須知道所有的策略類,並自行決定使用 哪一個策略,這意味着客戶端必須理解這些算法的區別,以便選擇恰當的算法
2、如果備選策略很多,對象的數據會很多
