概述
策略模式是一種行為設計模式, 它能讓你定義一系列算法, 并將每種算法分別放入獨立的類中, 以使算法的對象能夠相互替換
。
主要目的是通過定義相似的算法,替換if else 語句寫法
,并且可以隨時相互替換
結構
示例
策略模式在 Java 代碼中很常見。 它經常在各種框架中使用, 能在不擴展類的情況下向用戶提供改變其行為的方式。
javax.servlet.http.HttpServlet: ? service-()方法, 還有所有接受 Http-Servlet-Request和 Http-Servlet-Response對象作為參數的 do-XXX()方法。
識別方法: 策略模式可以 通過允許嵌套對象完成實際工作的方法,以及允許將該對象替換為不同對象的設置器來識別。
偽代碼實現
strategies
策略(strategies)的定義:所有具體策略的通用接口, 它聲明了一個上下文用于執行策略的方法
public interface PayStrategy {boolean pay(int paymentAmount);void collectPaymentDetails();
}
Concrete Strategies
具體策略 (Concrete Strategies): 實現上下文所用算法的各種不同變體。
PayByPayPal: 使用 PayPal 支付
public class PayByPayPal implements PayStrategy {@Overridepublic void collectPaymentDetails() {// todo}@Overridepublic boolean pay(int paymentAmount) {// todo}
PayByCreditCard: 使用信用卡支付
public class PayByCreditCard implements PayStrategy {@Overridepublic void collectPaymentDetails() {// todo}@Overridepublic boolean pay(int paymentAmount) {// todo}
Context
上下文 (Context): 維護指向具體策略的引用, 且僅通過策略接口與該對象進行交流。
public class Order {private int totalCost = 0;private boolean isClosed = false;// 提供一個計算的接口供客戶端使用。public void processOrder(PayStrategy strategy) {strategy.collectPaymentDetails();// Here we could collect and store payment data from the strategy.}}
Client
客戶端 (Client) 會創建一個特定策略對象并將其傳遞給上下文。
上下文則會提供一個設置器以便客戶端在運行時替換相關聯的策略。
public class Client {private static Map<Integer, Integer> priceOnProducts = new HashMap<>();private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));private static Order order = new Order();private static PayStrategy strategy;static {priceOnProducts.put(1, 2200);priceOnProducts.put(2, 1850);priceOnProducts.put(3, 1100);priceOnProducts.put(4, 890);}public static void main(String[] args) throws IOException {while (!order.isClosed()) {int cost;String continueChoice;do {System.out.print("Please, select a product:" + "\n" +"1 - Mother board" + "\n" +"2 - CPU" + "\n" +"3 - HDD" + "\n" +"4 - Memory" + "\n");int choice = Integer.parseInt(reader.readLine());cost = priceOnProducts.get(choice);System.out.print("Count: ");int count = Integer.parseInt(reader.readLine());order.setTotalCost(cost * count);System.out.print("Do you wish to continue selecting products? Y/N: ");continueChoice = reader.readLine();} while (continueChoice.equalsIgnoreCase("Y"));if (strategy == null) {System.out.println("Please, select a payment method:" + "\n" +"1 - PalPay" + "\n" +"2 - Credit Card");String paymentMethod = reader.readLine();// Client creates different strategies based on input from user,// application configuration, etc.if (paymentMethod.equals("1")) {strategy = new PayByPayPal();} else {strategy = new PayByCreditCard();}}// Order object delegates gathering payment data to strategy object,// since only strategies know what data they need to process a// payment.order.processOrder(strategy);System.out.print("Pay " + order.getTotalCost() + " units or Continue shopping? P/C: ");String proceed = reader.readLine();if (proceed.equalsIgnoreCase("P")) {// Finally, strategy handles the payment.if (strategy.pay(order.getTotalCost())) {System.out.println("Payment has been successful.");} else {System.out.println("FAIL! Please, check your data.");}order.setClosed();}}}
}
應用場景
當你想使用對象中各種不同的算法變體, 并希望能在運行時切換算法時
, 可使用策略模式。
策略模式讓你能夠將對象關聯至可以不同方式執行特定子任務的不同子對象, 從而以間接方式在運行時更改對象行為。
當你有許多僅在執行某些行為時略有不同的相似類時
, 可使用策略模式。
策略模式讓你能將不同行為抽取到一個獨立類層次結構中, 并將原始類組合成同一個, 從而減少重復代碼。
如果算法在上下文的邏輯中不是特別重要, 使用該模式能將類的業務邏輯與其算法實現細節隔離開來。
策略模式讓你
能將各種算法的代碼、 內部數據和依賴關系與其他代碼隔離開來
。 不同客戶端可通過一個簡單接口執行算法, 并能在運行時進行切換。
當類中使用了復雜條件運算符以在同一算法的不同變體中切換時, 可使用該模式。
策略模式將所有繼承自同樣接口的算法抽取到獨立類中, 因此不再需要條件語句。 原始對象并不實現所有算法的變體, 而是將執行工作委派給其中的一個獨立算法對象。
實現步驟
首先,從上下文類中找出修改頻率較高的算法
其次,聲明該算法所有變體的通用策略接口。將算法逐一抽取到各自的類中, 它們都必須實現策略接口。
之后,在上下文類中添加一個成員變量用于保存對于策略對象的引用
。
- 然后提供設置器以修改該成員變量。
- 上下文僅可通過策略接口同策略對象進行交互,
- 如有需要還可定義一個接口來讓策略訪問其數據。
最后,客戶端必須將上下文類與相應策略進行關聯, 使上下文可以預期的方式完成其主要工作。
優缺點
策略模式優點:
- 擴展性好,可以在不修改對象結構的情況下,為新的算法進行添加新的類進行實現;
- 靈活性好,可以對算法進行自由切換;
策略模式缺點:
- 使用策略類變多,會增加系統的復雜度。;
- 客戶端必須知道所有的策略類才能進行調用;