設計模式之策略模式(StrategyPattern)深入淺出
阿新 • • 發佈:2021-01-11
策略模式定義:是指定義了演算法家族、分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化不會影響到使用演算法的使用者
策略模式優點:可以避免多重分支的if...else...和switch語句
策略模式的使用場景:
- 假如系統中有很多類,而他們的區別僅僅在於他們的行為不同。
- 一個系統需要動態的在幾種演算法中選擇一種。
策略模式案例:
- JDK中Comparator介面中的int compare(T o1, T o2);方法,在Arrays、TreeMap裡可以自定義排序規則
Arrays.class
TreeMap.class
- Spring中的Resource介面
- Spring中InstantiationStrategy介面,主要對類進行初始化策略,有兩個策略實現類,它們不是平級而是繼承關係(策略模式中不同策略也可以繼承)
- CglibSubclassingInstantiationStrategy:Cglib的初始方式
- SimpleInstantiationStrategy:JDK的初始化方式
類圖
簡單程式碼案例(一)
案例:購買東西有活動時,會有抵用券、返現、拼團等減免金額的策略
優惠策略抽象介面
/** * @ClassName PromotionStrategy * @Author 周聰 * @Date 2021/1/10 17:58 * @Version 1.0 * @Description 優惠策略的抽象 */ public interface PromotionStrategy {/** * 執行優惠 */ void doPromotion(); }
沒有優惠
/** * @ClassName EmptyStrategy * @Author 周聰 * @Date 2021/1/10 17:59 * @Version 1.0 * @Description 無優惠 */ public class EmptyStrategy implements PromotionStrategy{ @Override public void doPromotion() { System.out.println("無促銷活動"); } }
抵用券
/** * @ClassName CouponStrategy * @Author 周聰 * @Date 2021/1/10 18:01 * @Version 1.0 * @Description 優惠券抵扣 */ public class CouponStrategy implements PromotionStrategy { @Override public void doPromotion() { System.out.println("領取優惠券,課程的價格直接減去優惠券面值抵扣"); } }
返現
/** * @ClassName CashbackStrategy * @Author 周聰 * @Date 2021/1/10 18:04 * @Version 1.0 * @Description 返現 */ public class CashbackStrategy implements PromotionStrategy { @Override public void doPromotion() { System.out.println("返現促銷,返回的金額轉到支付寶賬號"); } }
拼團
/** * @ClassName GroupBuyStrategy * @Author 周聰 * @Date 2021/1/10 18:05 * @Version 1.0 * @Description 團購 */ public class GroupBuyStrategy implements PromotionStrategy{ @Override public void doPromotion() { System.out.println("拼團,滿20人成團,全團享受團購價格"); } }
優惠活動
/** * @ClassName PromotionActivity * @Author 周聰 * @Date 2021/1/10 18:07 * @Version 1.0 * @Description 優惠活動 */ public class PromotionActivity { PromotionStrategy promotionStrategy; public PromotionActivity(PromotionStrategy promotionStrategy){ this.promotionStrategy = promotionStrategy; } /** * 執行優惠活動 */ public void execute(){ this.promotionStrategy.doPromotion(); } }
測試一
public static void main(String[] args) { // 618優惠券活動 PromotionActivity activity618 = new PromotionActivity(new CouponStrategy()); activity618.execute(); // 雙11返現活動 PromotionActivity activity1111 = new PromotionActivity(new CashbackStrategy()); activity1111.execute(); }
測試二
public static void main(String[] args) { PromotionActivity promotionActivity = null; String promotionKey = "COUPON"; if (StringUtils.equals(promotionKey,"COUPON")){ promotionActivity = new PromotionActivity(new CouponStrategy()); }else if (StringUtils.equals(promotionKey,"CASHBACK")){ promotionActivity = new PromotionActivity(new CashbackStrategy()); } // ....... promotionActivity.execute(); }
簡單實現完發現還可以通過單例模式+簡單工廠模式簡化程式碼,用唯一標誌選擇策略
工廠類
/** * @ClassName PromotionStrategyFactory * @Author 周聰 * @Date 2021/1/10 18:28 * @Version 1.0 * @Description 簡單工廠+ 註冊式餓漢式單例 */ public class PromotionStrategyFactory { private static final Map<String,PromotionStrategy> PROMOTION_STRATEGY_MAP = new ConcurrentHashMap<String,PromotionStrategy>(); private static final PromotionStrategy NON_PROMOTION = new EmptyStrategy(); static { PROMOTION_STRATEGY_MAP.put(PromotionKey.COUPON,new CouponStrategy()); PROMOTION_STRATEGY_MAP.put(PromotionKey.CASHBACK,new CashbackStrategy()); PROMOTION_STRATEGY_MAP.put(PromotionKey.GROUPBUY,new GroupBuyStrategy()); } private PromotionStrategyFactory(){} public static PromotionStrategy getPromotionStrategy(String promotionKey){ PromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey); return promotionStrategy == null ? NON_PROMOTION : promotionStrategy; } private interface PromotionKey{ String COUPON = "COUPON"; String CASHBACK = "CASHBACK"; String GROUPBUY = "GROUPBUY"; } }
測試
public static void main(String[] args) { String promotionKey = "GROUPBUY"; PromotionActivity promotionActivity = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey)); promotionActivity.execute(); }
附上類結構圖
簡單程式碼案例(二)
案例:使用者下單支付,可以選擇多種支付渠道
支付抽象類
/** * @ClassName Payment * @Author 周聰 * @Date 2021/1/10 18:55 * @Version 1.0 * @Description 支付抽象 */ public abstract class Payment { /** * 支付資訊 * @return */ public abstract String getName(); /** * 餘額 * @param uid * @return */ protected abstract double queryBalance(String uid); /** * 支付 * @param uid * @param amount * @return */ public MsgResult pay(String uid,double amount){ if (queryBalance(uid) < amount){ return new MsgResult(500,"支付失敗","餘額不足"); }else { return new MsgResult(200,"支付成功","支付金額: " + amount); } } }
支付寶支付
/** * @ClassName AliPay * @Author 周聰 * @Date 2021/1/10 18:54 * @Version 1.0 * @Description 支付寶支付 */ public class AliPay extends Payment{ @Override public String getName() { return "支付寶"; } @Override protected double queryBalance(String uid) { return 900; } }
京東白條
/** * @ClassName JDPay * @Author 周聰 * @Date 2021/1/10 19:45 * @Version 1.0 * @Description */ public class JDPay extends Payment{ @Override public String getName() { return "京東白條"; } @Override protected double queryBalance(String uid) { return 500; } }
銀聯支付
/** * @ClassName UnionPay * @Author 周聰 * @Date 2021/1/10 19:47 * @Version 1.0 * @Description 銀聯支付 */ public class UnionPay extends Payment { @Override public String getName() { return "銀聯支付"; } @Override protected double queryBalance(String uid) { return 120; } }
微信支付
/** * @ClassName WechatPay * @Author 周聰 * @Date 2021/1/10 19:46 * @Version 1.0 * @Description */ public class WechatPay extends Payment { @Override public String getName() { return "微信支付"; } @Override protected double queryBalance(String uid) { return 256; } }
返回訊息類
/** * @ClassName MsgResult * @Author 周聰 * @Date 2021/1/10 18:49 * @Version 1.0 * @Description 返回訊息類 */ public class MsgResult { private int code; private Object data; private String msg; public MsgResult(int code, Object data, String msg) { this.code = code; this.data = data; this.msg = msg; } @Override public String toString() { return "MsgResult{" + "code=" + code + ", data=" + data + ", msg='" + msg + '\'' + '}'; } }
訂單類
/** * @ClassName Order * @Author 周聰 * @Date 2021/1/10 18:48 * @Version 1.0 * @Description */ public class Order { private String uid; private String orderId; private double amount; public Order(String uid, String orderId, double amount) { this.uid = uid; this.orderId = orderId; this.amount = amount; } public MsgResult pay(String payKey){ Payment payment = PayStrategy.get(payKey); System.out.println("歡迎使用" + payment.getName()); System.out.println("本次交易金額為:" + amount + ",開始扣款..."); return payment.pay(uid,amount); } @Override public String toString() { return "Order{" + "uid='" + uid + '\'' + ", orderId='" + orderId + '\'' + ", amount=" + amount + '}'; } }
支付策略工廠
/** * @ClassName PayStrategy * @Author 周聰 * @Date 2021/1/10 19:51 * @Version 1.0 * @Description 支付策略工廠 */ public class PayStrategy { public static final String ALI_PAY = "AliPay"; public static final String JD_PAY = "JDPay"; public static final String WECHAT_PAY = "WchatPay"; public static final String UNION_PAY = "UnionPay"; public static final String DEFAULT_PAY = "AliPay"; private static Map<String,Payment> payStrategy = new HashMap<String,Payment>(); static { payStrategy.put(ALI_PAY,new AliPay()); payStrategy.put(JD_PAY,new JDPay()); payStrategy.put(WECHAT_PAY,new WechatPay()); payStrategy.put(UNION_PAY,new UnionPay()); payStrategy.put(DEFAULT_PAY,new AliPay()); } public static Payment get(String payKey){ if (!payStrategy.containsKey(payKey)){ return payStrategy.get(DEFAULT_PAY); } return payStrategy.get(payKey); } }
測試
/** * @ClassName PayStrategyTest * @Author 周聰 * @Date 2021/1/10 19:48 * @Version 1.0 * @Description */ public class PayStrategyTest { public static void main(String[] args) { Order order = new Order("1", "2021110", 1342.45); MsgResult result = order.pay(PayStrategy.ALI_PAY); System.out.println(result); } }
附上類結構圖:
總結
策略模式的優點:
- 策略模式符合開閉原則。
- 避免使用多重條件轉移語句,如if...else...語句、switch語句。
- 使用策略模式可以提高演算法的保密性和安全性。
策略模式的缺點:
- 客戶端必須知道所有的策略,並且使用者自行決定使用哪一個策略類。
- 程式碼中會產生非常多策略類,增加維護難度。
以上對策略模式的介紹到此結束,歡迎批評指正。附:原始碼地址