設計模式—–策略模式
- 2019 年 11 月 2 日
- 筆記
策略模式
定義
所謂策略模式就是定義了演算法族,分別封裝起來,讓他們之前可以互相轉換,此模式讓該演算法的變化獨立於使用演算法的客戶
理解
**策略這個詞應該怎麼理解,打個比方說,我們出門的時候會選擇不同的出行方式,比如騎自行車、坐公交、坐火車、坐飛機、坐火箭等等,這些出行方式,每一種都是一個策略 **
**再比如我們去逛商場,商場現在正在搞活動,有打折的、有滿減的、有返利的等等,其實不管商場如何進行促銷,說到底都是一些演算法,這些演算法本身只是一種策略,並且這些演算法是隨時都可能互相替換的,比如針對同一件商品,今天打八折、明天滿100減30,這些策略間是可以互換的 **
UML類圖

**其中,Context是上下文,用一個ConcreteStrategy來配置,維護一個對Strategy對象的引用;Strategy是策略類,用於定義所有支援演算法的公共介面;ConcreteStrategy是具體策略類,封裝了具體的演算法或行為,繼承於Strategy **
解釋
1.Context 上下文
Context上下文角色,也叫Context封裝角色,起承上啟下的作用,屏蔽高層模組對策略、演算法的直接訪問,封裝可能存在的變化
public class Context { Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } /** * 上下文介面 */ public void contextInterface(){ strategy.algorithmInterface(); } }2.Strategy 策略角色
抽象策略角色,是對策略、演算法家族的抽象,通常為介面,定義每個策略或演算法必須具有的方法和屬性。algorithm是「運演算法則」的意思
public interface Strategy { /** * 演算法方法 */ public void algorithmInterface(); }3.具體策略角色
用於實現抽象策略中的操作,即實現具體的演算法,下方用print代替。測試類共3個ConcreteStrategy,其它兩個類與ConcreteStrategyA同理,就不再贅述了
class ConcreteStrategyA implements Strategy{ @Override public void algorithmInterface() { System.out.println("策略A的具體演算法實現"); } }4.Client 客戶端
下面依次更換策略,測試一下策略模式
public class StrategyClient { public static void main(String[] args) { Context context; context = new Context(new ConcreteStrategyA()); context.contextInterface(); context = new Context(new ConcreteStrategyB()); context.contextInterface(); context = new Context(new ConcreteStrategyC()); context.contextInterface(); } }運行結果如下:

實例一:
我們都知道排序演算法有很多種,但是什麼時候選擇冒泡排序,什麼時候選擇選擇排序,什麼時候選擇插入排序,所以這裡用排序演算法來演示策略模式的實現
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. 上下文類
首先聲明一個CashSuper對象,通過構造方法,傳入具體的收費策略,getResult()方法的功能為根據收費策略的不同獲得計算結果。
public class CashContext { private CashSuper cashSuper; public CashContext(CashSuper cashSuper) { this.cashSuper = cashSuper; } public double getResult(double money) { return cashSuper.acceptCash(money); } }2. 現金收費抽象類
策略類,為抽象類,抽象出收費的方法供子類實現。
public abstract class CashSuper { public abstract double acceptCash(double money); }3. 正常收費子類
沒有任何活動的情況,正常收費,返回原價。
public class CashNormal extends CashSuper { @Override public double acceptCash(double money) { return money; } }4. 打折收費子類
打折活動,根據折扣返回打折後的價格。
public class CashRebate extends CashSuper { private double moneyRebate = 1; //折扣 public CashRebate(double moneyRebate) { this.moneyRebate = moneyRebate; } @Override public double acceptCash(double money) { return money * moneyRebate; } }5. 返利收費子類
返利活動,輸入返利條件和返利值,比如滿300返100,moneyCoditation為300,moneyReturn為100。
result = money - Math.floor(money / moneyConditation) * moneyReturn; 的意思為,如果當前金額大於等於返利條件,則使用當前金額減去返利值
public class CashReturn extends CashSuper { private double moneyConditation = 0.0; //返利條件 private double moneyReturn = 0.0d; //返利值 public CashReturn(double moneyConditation, double moneyReturn) { this.moneyConditation = moneyConditation; this.moneyReturn = moneyReturn; } @Override public double acceptCash(double money) { double result = money; if (money >= moneyConditation) { result = money - Math.floor(money / moneyConditation) * moneyReturn; } return result; } }6. Client客戶端
下面寫一個簡單的程式測試一下上方編寫的程式碼。
public class Client { public static void main(String[] args) { CashContext cashContext = null; Scanner scanner = new Scanner(System.in); System.out.print("請輸入打折方式(1/2/3):"); int in = scanner.nextInt(); String type = ""; switch (in) { case 1: cashContext = new CashContext(new CashNormal()); type += "正常收費"; break; case 2: cashContext = new CashContext(new CashReturn(300, 100)); type += "滿300返100"; break; case 3: cashContext = new CashContext(new CashRebate(0.8)); type += "打8折"; break; default: System.out.println("請輸入1/2/3"); break; } double totalPrices = 0; System.out.print("請輸入單價:"); double price = scanner.nextDouble(); System.out.print("請輸入數量:"); double num = scanner.nextDouble(); totalPrices = cashContext.getResult(price * num); System.out.println("單價:" + price + ",數量:" + num + ",類型:" + type + ",合計:" + totalPrices); scanner.close(); } }正常收費結果如下:

應用
1.何時使用
- 一個系統有許多類,而區分它們的只是他們直接的行為時
2.方法
- 將這些演算法封裝成一個一個的類,任意的替換
3.優點
- 演算法可以自由切換
- 避免使用多重條件判斷(如果不用策略模式我們可能會使用多重條件語句,不利於維護)
- 擴展性良好,增加一個策略只需實現介面即可
4.缺點
- 策略類數量會增多,每個策略都是一個類,復用的可能性很小
- 所有的策略類都需要對外暴露
5.使用場景
- 多個類只有演算法或行為上稍有不同的場景
- 演算法需要自由切換的場景
- 需要屏蔽演算法規則的場景
6.應用實例
- 出行方式,自行車、汽車等,每一種出行方式都是一個策略
- 商場促銷方式,打折、滿減等
- Java AWT中的LayoutManager,即布局管理器
7.注意事項
- 如果一個系統的策略多於四個,就需要考慮使用混合模式來解決策略類膨脹的問題

