點外賣,讓我想起了 策略模式
今天給大家分享的是策略模式,具體內容大綱如下:
生活案例
在這互聯網時代,尤其是在城市中,有一幫騎著電瓶車,穿梭在大街小巷中,這幫人就是外賣小哥。
對于點外賣,我也點過不少。有一次,外賣下單的時候,我突然聯想到了一個設計模式---策略模式。
策略模式是個啥?
策略模式:英文為Strategy Pattern,是指定義了算法家族、分別封裝起來,讓他們之間可以相互替換,此設計模式讓算法的變化不會影響到使用算法的用戶。
英文
Define a family of algorithms,encapsulate each one,and make them interchangeable.
大致意思:定義一組算法,將每個算法都封裝起來,并且使它們之間可以互換。
在策略模式中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬于行為型模式。
策略模式通用代碼
java代碼實現如下:
- class Client {
- //抽象策略類 Strategy
- interface IStrategy {
- void algorithm();
- }
- //具體策略類 ConcreteStrategy
- static class ConcreteStrategyA implements IStrategy {
- @Override
- public void algorithm() {
- System.out.println("Strategy A");
- }
- }
- //具體策略類 ConcreteStrategy
- static class ConcreteStrategyB implements IStrategy {
- @Override
- public void algorithm() {
- System.out.println("Strategy B");
- }
- }
- //上下文環境
- static class Context {
- private IStrategy mStrategy;
- public Context(IStrategy strategy) {
- this.mStrategy = strategy;
- }
- public void algorithm() {
- this.mStrategy.algorithm();
- }
- }
- public static void main(String[] args) {
- //選擇一個具體策略
- IStrategy strategy = new ConcreteStrategyA();
- //來一個上下文環境
- Context context = new Context(strategy);
- //客戶端直接讓上下文環境執行算法
- context.algorithm();
- }
- }
從上面的通用代碼,我們可以得知其UML圖。
策略模式UML圖
策略模式中的角色
從 UML 類圖中,我們可以看到,策略模式主要包含三種角色:
- 上下文角色(Context):用來操作策略的上下文環境,屏蔽高層模塊(客戶端)對策略,算法的直接訪問,封裝可能存在的變化;
- 抽象策略角色(Strategy):規定策略或算法的行為;
- 具體策略角色(ConcreteStrategy):具體的策略或算法實現;
策略模式優缺點
優點
- 策略模式符合開閉原則
- 避免使用多重轉換語句,比如:if...else、switch語句。
- 使用策略模式可以提高算法的保密性和安全性
缺點
- 客戶端必須知道所有策略,并且自行決定使用哪一種策略
- 代碼中產生非常多的策略類,增加后期維護難度
策略模式使用場景
在日常開發中,策略模式適用于以下三種場景:
- 針對同一類型問題,有多重處理方式,每一種都能獨立解決問題。
- 算法需要自由切換的場景。
- 需要屏蔽算法規則的場景
這個說起來,還是不太好理解。
下面,我們就來使用生活案例來實現,讓大家知道策略模式到底是怎么使用的。
支付案例代碼重構,三個版本
外面下單,選擇支付方式的時候,我覺這個功能,我們可以模仿著使用策略模式來實現一下。下面我們通過三個版本的迭代來實現,很有意思的。
第一版
先定義一個抽象類Pay:
- //定義抽象類,我們可以把一些共用功能放在抽象類里實現
- //比如:可用余額和本次支付金額進行比較,統一返回“支付失敗”
- public abstract class Pay {
- abstract void doPay();
- }
下面模擬三種支付方式:
- public class AliPay extends Pay {
- @Override
- public void doPay() {
- System.out.println("使用支付寶支付");
- }
- }
- public class UnionPay extends Pay {
- @Override
- public void doPay() {
- System.out.println("使用銀聯支付");
- }
- }
- public class WechatPay extends Pay {
- @Override
- public void doPay() {
- System.out.println("使用微信支付");
- }
- }
我們再來進行支付:
- public class PayTest {
- public static void main(String[] args) {
- //把選擇權交給了用戶
- Order order = new Order(new WechatPay());
- order.pay();*
- }
- }
運行結果:
- 使用微信支付
這樣我們就使用策略模式實現了簡單版本的支付,但是其中有個很不爽的地方,就是每次還得自己手工new一個支付方式的對象。鑒于此,我們對第一版進行重構。
第二版
前面的實現都不變,變化的是發起支付的時候,只要前端傳一個key過來就可以了,實現如下:
- public class PayTest {
- public static void main(String[] args) {
- String payKey = "Wechat";
- Order order = null;
- //通過判斷前端傳過來的key,判斷使用哪種支付方式
- if (payKey.equals("Ali")) {
- order = new Order(new AliPay());
- } else if (payKey.equals("Wechat")) {
- order = new Order(new WechatPay());
- } else if (payKey.equals("union")) {
- order = new Order(new UnionPay());
- }else {
- //給出一個默認方式
- order = new Order(new WechatPay());
- }
- order.pay();
- }
- }
運行結果
- 使用微信支付
這樣我們就實現了,通過前端傳過來的key,然后選出對應的支付方式。但是問題又來了,如果支付方式不斷增多,那這里的if...else豈不是會越來越多嗎?后續維護成本不是越來越大嗎?
于是,第三版就有了。
第三版
在第二版中,會出現大量的if...else,會給后續的代碼維護帶來不便,于是在這一版中,我們對其進行重構,引入了注冊式單例模式。
- import java.util.HashMap;
- import java.util.Map;
- public enum PayStrategyEnum {
- ALI_PAY("Ali"),
- WECHAT_PAY("Wechat"),
- UNION_PAY("union"),
- //默認使用微信支付
- DEFAULT_PAY("Wechat");
- private String key;
- PayStrategyEnum(String key) {
- this.key = key;
- }
- private static final Map<String, Pay> payKeyMap = new HashMap();
- static {
- payKeyMap.put(ALI_PAY.key, new AliPay());
- payKeyMap.put(WECHAT_PAY.key, new WechatPay());
- payKeyMap.put(UNION_PAY.key, new UnionPay());
- payKeyMap.put(DEFAULT_PAY.key, new WechatPay());
- }
- public static Pay getPay(String payKey) {
- if (!payKeyMap.containsKey(payKey)) {
- return payKeyMap.get(DEFAULT_PAY.key);
- }
- return payKeyMap.get(payKey);
- }
- }
然后,在訂單支付的時候就變成了這樣了:
- public class PayTest {
- public static void main(String[] args) {
- String payKey = "Wechat";
- Order order = new Order(PayStrategyEnum.getPay(payKey));
- order.pay();
- }
- }
運行結果
- 使用微信支付
這樣,我們就成功的規避了大量的if...else了,爽歪歪!
其實,上面三個版本的代碼,是不是覺得很爽,這就是設計模式的強大之處。
PS:關于上面的三個版本,其實我們還可以繼續完善,繼續重構,感興趣的你可以去試試如何繼續重構。
總結
好了,今天的策略模式就到這里。其實,設計模式在大多數情況下,是不會單獨存在的,都是使用多種設計模式混合起來使用的。
策略模式使用的就是面向對象的繼承和多態機制,從而實現同一行為在不同場景下不同實現。
最好記的案例:
我們可以使用不同的交通工具去北京玩
坐飛機、坐高鐵、坐汽車、開車、騎車。方式很多,你想選哪一條就選那一條。
最后用一句話來總結策略模式:
條條大路通羅馬
本文轉載自微信公眾號「Java后端技術全?!?,可以通過以下二維碼關注。轉載本文請聯系Java后端技術全棧公眾號。