1. 程式人生 > 實用技巧 >設計模式之策略模式(StrategyPattern)深入淺出

設計模式之策略模式(StrategyPattern)深入淺出

策略模式定義:是指定義了演算法家族、分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化不會影響到使用演算法的使用者

策略模式優點:可以避免多重分支的if...else...和switch語句

策略模式的使用場景:

  • 假如系統中有很多類,而他們的區別僅僅在於他們的行為不同。
  • 一個系統需要動態的在幾種演算法中選擇一種。

策略模式案例:

  • JDK中Comparator介面中的int compare(T o1, T o2);方法,在Arrays、TreeMap裡可以自定義排序規則

Arrays.class

TreeMap.class

  • Spring中的Resource介面
  • Spring中InstantiationStrategy介面,主要對類進行初始化策略,有兩個策略實現類,它們不是平級而是繼承關係(策略模式中不同策略也可以繼承)
  1. CglibSubclassingInstantiationStrategy:Cglib的初始方式
  2. 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語句。
  • 使用策略模式可以提高演算法的保密性和安全性。

策略模式的缺點:

  • 客戶端必須知道所有的策略,並且使用者自行決定使用哪一個策略類。
  • 程式碼中會產生非常多策略類,增加維護難度。

以上對策略模式的介紹到此結束,歡迎批評指正。附:原始碼地址