在理解策略模式以前咱們假設有這樣一個需求場景:咱們在寫訂單支付場景的代碼時,客戶能夠選擇多種支付方式,有銀聯支付、支付寶支付、微信支付、京東白條等等。而後咱們就極可能就會編寫出相似下面這樣的代碼:html
/** * 訂單類,擁有一個支付方法 * * @author eamon.zhang * @date 2019-11-06 上午9:18 */ public class Order { // 訂單id private String orderId; // 支付方式 private String payType; // 訂單金額 private long amount; public Order(String orderId, String payType, long amount) { this.orderId = orderId; this.payType = payType; this.amount = amount; } /** * 訂單支付方法 * * @return */ public boolean pay() { // 是否支付成功 boolean payment = false; if ("aliPay".equals(payType)) { System.out.println("用戶選擇 支付寶 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); payment = true; } else if ("unionPay".equals(payType)) { System.out.println("用戶選擇 銀聯 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); payment = true; } else if ("jdPay".equals(payType)) { System.out.println("用戶選擇 京東 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); payment = true; } else if ("wechatPay".equals(payType)) { System.out.println("用戶選擇 微信 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); payment = true; } return payment; } }
客戶端:前端
@Test public void test() { String orderId = "123"; String payType = "aliPay"; long amount = 200; // 建立訂單 Order order = new Order(orderId, payType, amount); // 支付 order.pay(); }
結果:java
用戶選擇 支付寶 支付,訂單號爲:123 ,支付金額:200
能夠看出這段代碼在邏輯上沒有問題,也可以很好的運行;git
可是存在一個問題:將全部的支付方式都寫在同一個方法裏面,顯得有點臃腫,還帶來了一個擴展的問題,若是咱們再增長一種支付方式,咱們就不得不修改這段代碼,再增長一條 if...else
,這就下降了代碼的可維護性。違背了開閉原則。算法
那可否有一種方法能讓咱們既不修改主要邏輯代碼的前提下讓代碼變得更優雅也能很好的對其進行擴展呢?那不防咱們一塊兒來看看今天的主角:策略模式後端
策略模式屬於對象的行爲模式。其用意是針對一組算法,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換。策略模式使得算法能夠在不影響到客戶端的狀況下發生變化,也就是在策略模式(Strategy Pattern
)中,一個類的行爲或其算法能夠在運行時更改。設計模式
策略模式中通常會涉及到三個角色:安全
IStrategy
:用來約束一系列具體的策略算法,策略上下文角色 ConcreteStrategy
使用此策略接口來調用具體的策略所實現的算法。ConcreteStrategy
:具體的策略實現,即具體的算法實現。StrategyContext
:策略上下文,負責和具體的策略實現交互,一般策略上下文對象會持有一個真正的策略實現對象,策略上下文還可讓具體的策略實現從其中獲取相關數據,回調策略上下文對象的方法。先建立抽象策略接口 IStrategy
:微信
/** * 策略類抽象接口,具體策略實現由其子類來實現 * * @author EamonZzz * @date 2019-11-02 16:12 */ public interface IStrategy { /** * 定義的抽象算法方法 來約束具體的算法實現方法 */ void algorithmMethod(); }
建立具體的策略實現類 ConcreteStrategyA
:前後端分離
/** * 策略具體實現類A * * @author EamonZzz * @date 2019-11-02 16:48 */ public class ConcreteStrategyA implements IStrategy { /** * 具體的算法體現 */ @Override public void algorithmMethod() { System.out.println("this is ConcreteStrategyA method..."); } }
建立具體的策略實現類 ConcreteStrategyB
:
/** * 策略具體實現類B * * @author EamonZzz * @date 2019-11-02 16:48 */ public class ConcreteStrategyB implements IStrategy { /** * 具體的算法體現 */ @Override public void algorithmMethod() { System.out.println("this is ConcreteStrategyB method..."); } }
建立具體的策略實現類 ConcreteStrategyC
:
/** * 策略具體實現類C * * @author EamonZzz * @date 2019-11-02 16:48 */ public class ConcreteStrategyC implements IStrategy { /** * 具體的算法體現 */ @Override public void algorithmMethod() { System.out.println("this is ConcreteStrategyC method..."); } }
建立策略上下文 StrategyContext
:
/** * 策策略上下文,負責和具體的策略實現交互,一般策略上下文對象會持有一個真正的策略實現對象, * 策略上下文還可讓具體的策略實現從其中獲取相關數據,回調策略上下文對象的方法。 * * @author EamonZzz * @date 2019-11-02 16:11 */ public class StrategyContext { /** * 策略實現的引用 */ private IStrategy strategy; /** * 構造器注入具體的策略類 * * @param iStrategy 策略實現的引用 */ public StrategyContext(IStrategy iStrategy) { this.strategy = iStrategy; } public void contextMethod() { // 調用策略實現的方法 strategy.algorithmMethod(); } }
最後編寫測試類來測試一下結果
/** * @author EamonZzz * @date 2019-11-02 16:53 */ public class StrategyContextTest { @Test public void test(){ // 1. 建立具體的策略實現 IStrategy strategy = new ConcreteStrategyA(); // 2. 在建立策略上下文的同時,將具體的策略實現對象注入到策略上下文當中 StrategyContext ctx = new StrategyContext(strategy); // 3. 調用上下文對象的方法來完成對具體策略實現的回調 ctx.contextMethod(); } }
控制檯輸出:
this is ConcreteStrategyA method...
在簡單的瞭解了策略模式以後,再看看文章開頭的實例場景,咱們使用策略模式來對其進行改造:
咱們將訂單支付邏輯中的各類支付場景算法單獨抽離出來:
先建立抽象的支付接口 Payment
,讓各類平臺的支付邏輯類都實現該接口,達到統一調用的目的:
/** * 統一支付接口 * * @author eamon.zhang * @date 2019-11-06 上午9:44 */ public interface Payment { boolean pay(String orderId, long amount); }
而後分別建立支付寶支付類 AliPay
:
/** * 支付寶支付 * * @author eamon.zhang * @date 2019-11-06 上午9:48 */ public class AliPay implements Payment { @Override public boolean pay(String orderId, long amount) { System.out.println("用戶選擇 支付寶 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); return true; } }
建立微信支付類 WeChatPay
:
/** * 微信支付 * * @author eamon.zhang * @date 2019-11-06 上午9:49 */ public class WeChatPay implements Payment { @Override public boolean pay(String orderId, long amount) { System.out.println("用戶選擇 微信 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); return true; } }
建立銀聯支付類 UnionPay
:
/** * 銀聯支付 * * @author eamon.zhang * @date 2019-11-06 上午9:50 */ public class UnionPay implements Payment { @Override public boolean pay(String orderId, long amount) { System.out.println("用戶選擇 銀聯 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); return true; } }
而後建立訂單類 Order
:
/** * 訂單類,至關於 策略上下文角色 * * @author eamon.zhang * @date 2019-11-06 上午9:43 */ public class Order { // 訂單id private String orderId; // 金額 private long amount; // 具體支付類型的引用 private Payment payType; public Order(String orderId, Payment payType, long amount) { this.orderId = orderId; this.payType = payType; this.amount = amount; } /** * 訂單支付方法 * * @return */ public boolean pay() { boolean paySuccess; // 調用支付接口 paySuccess = payType.pay(orderId, amount); if (!paySuccess) { // 支付失敗邏輯 System.out.println("支付失敗!"); } return paySuccess; } }
最後建立咱們的客戶端模擬調用:
@Test public void test() { // 建立支付策略 Payment weChatPay = new WeChatPay(); // 建立策略上下文(訂單),並將具體的策略實現注入 String orderId = "123456"; long amount = 150; Order order = new Order(orderId, weChatPay, amount); // 調用具體支付策略邏輯 order.pay(); }
運行結果:
用戶選擇 微信 支付,訂單號爲:123456 ,支付金額:150
這樣就對訂單支付場景完成了一個基本的改造,訂單支付的選擇權直接在用戶選擇支付方式時建立,訂單支付方法中統一進行調用;當咱們須要新增長一種支付方式時,就能夠直接繼承 Payment
抽象支付策略接口,而後實現支付方法,好比咱們如今增長了一種京東白條支付 JdPay
就能夠這樣寫:
/** * 京東支付 * * @author eamon.zhang * @date 2019-11-06 上午9:49 */ public class JdPay implements Payment { @Override public boolean pay(String orderId, long amount) { System.out.println("用戶選擇 京東 支付,訂單號爲:" + orderId + " ,支付金額:" + amount); return true; } }
一樣的在客戶端調用:
/** * @author eamon.zhang * @date 2019-11-06 上午10:00 */ public class OrderTest { @Test public void test() { // 建立支付策略 Payment jdPay = new JdPay(); // 建立策略上下文(訂單),並將具體的策略實現注入 String orderId = "123456"; long amount = 150; Order order = new Order(orderId, jdPay, amount); // 調用具體支付策略邏輯 order.pay(); } }
運行結果:
用戶選擇 京東 支付,訂單號爲:123456 ,支付金額:150
能夠看到,在通過使用 策略模式 改造以後,咱們的訂單支付的擴展變得很是的容易,增長支付方式時,直接建立一個類並實現支付邏輯便可,不須要再修改咱們的主類 Order
。這就遵循了 開閉原則 。
上面第一次改造,只能勉強說明 策略模式 給實際業務帶來的好處,可是回到咱們現實的支付場景中,用戶選擇支付方式而且支付的操做都是在前端頁面進行的,並且如今大都使用 先後端分離 的模式來進行開發,並不能像 JSP
那樣,能夠在頁面中建立 Java
對象,在先後端分離的場景中,全部參數都是從頁面構建好鍵值對傳入,其基本類型爲數字、字符串等等。因此咱們能夠再結合以前說的 工廠模式 進行改造,使其更適合現實場景。
建立支付策略的工廠類 PayStrategyFactory
:
/** * 建立支付策略的工廠類 * * @author eamon.zhang * @date 2019-11-06 上午10:32 */ public class PayStrategyFactory { // 支付方式常量 public static final String ALI_PAY = "aliPay"; public static final String JD_PAY = "jdPay"; public static final String WECHAT_PAY = "wechatPay"; public static final String UNION_PAY = "unionPay"; // 支付方式管理集合 private static Map<String, Payment> PAYMENT_STRATEGY_MAP = new HashMap<>(); static { PAYMENT_STRATEGY_MAP.put(ALI_PAY, new AliPay()); PAYMENT_STRATEGY_MAP.put(JD_PAY, new JdPay()); PAYMENT_STRATEGY_MAP.put(WECHAT_PAY, new WeChatPay()); PAYMENT_STRATEGY_MAP.put(UNION_PAY, new UnionPay()); } /** * 獲取支付方式類 * * @param payType 前端傳入支付方式 * @return */ public static Payment getPayment(String payType) { Payment payment = PAYMENT_STRATEGY_MAP.get(payType); if (payment == null) { throw new NullPointerException("支付方式選擇錯誤!"); } return payment; } }
而後結合實際狀況對 Order
類進行修改,使其支付方式的選擇權交由用戶作支付動做時進行選擇,由於提交訂單後能夠選擇不支付,這時候訂單能夠先建立:
/** * 訂單類,至關於 策略上下文角色 * * @author eamon.zhang * @date 2019-11-06 上午9:43 */ public class Order { // 訂單id private String orderId; // 金額 private long amount; public Order(String orderId, long amount) { this.orderId = orderId; this.amount = amount; } /** * 訂單支付方法 * * @return */ public boolean pay(String payType) { boolean paySuccess; Payment payment = PayStrategyFactory.getPayment(payType); // 調用支付接口 paySuccess = payment.pay(orderId, amount); if (!paySuccess) { // 支付失敗邏輯 System.out.println("支付失敗!"); } return paySuccess; } }
最後建立測試代碼:
@Test public void test() { // 前端傳入的參數 String orderId = "01000005"; String payType = PayStrategyFactory.ALI_PAY; long amount = 190; // 建立策略上下文(訂單),並將具體的策略實現注入 Order order = new Order(orderId, amount); // 實際狀況是 在支付的時候選擇支付方式,由於有可能先提交了訂單,後面再付款 order.pay(payType); }
測試結果:
用戶選擇 支付寶 支付,訂單號爲:01000005 ,支付金額:190
這樣纔算完成了一個比較友好且更貼合實際業務狀況的業務代碼。固然這只是簡單的一個示例,現實中,代碼的邏輯會很是複雜;現實中各類設計模式一般咱們會配合進行使用,策略模式的使用頻率也很是的高,但願你們看完以後可以理解並運用。
在有多種算法類似的狀況下,解決使用 if...else
所帶來的複雜和難以維護的問題
在 JDK 源碼中也有很是多的 策略模式 的運用,比較經常使用的就是 Comparator
接口,它有很是多的實現方法,源碼中也有不少地方對其進行引用
若是有興趣,可使用工具跟進查看一下,這裏就不作過多的分析了
本文源碼:https://git.io/JeaYZ
歡迎star
推薦閱讀和參考資料:
https://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html
https://www.cnblogs.com/lewis0077/p/5133812.html