行爲型模式之策略模式


在現實生活中常常遇到實現某種目標存在多種策略可供選擇的情況,例如,出行旅遊可以乘坐飛機、乘坐火車、騎自行車或自己開私家車等,超市促銷可以釆用打折、送商品、送積分等方法。

在軟件開發中也常常遇到類似的情況,當實現某一個功能存在多種算法或者策略,我們可以根據環境或者條件的不同選擇不同的算法或者策略來完成該功能,如果使用多重條件轉移語句實現(即硬編碼),不但使條件語句變得很複雜,而且增加、刪除或更換算法要修改原代碼,不易維護,違背開閉原則。如果採用策略模式就能很好解決該問題。

策略模式的定義

策略(Strategy)模式的定義:該模式定義了一系列算法,並將每個算法封裝起來,使它們可以相互替換,且算法的變化不會影響使用算法的客戶。策略模式屬於對象行爲模式,它通過對算法進行封裝,把使用算法的責任和算法的實現分割開來,並委派給不同的對象對這些算法進行管理。

策略模式的結構

策略模式是準備一組算法,並將這組算法封裝到一系列的策略類裏面,作爲一個抽象策略類的子類。策略模式的重心不是如何實現算法,而是如何組織這些算法,從而讓程序結構更加靈活,具有更好的維護性和擴展性。

策略模式的主要角色如下:
抽象策略(Strategy)類:定義了一個公共接口,各種不同的算法以不同的方式實現這個接口,環境角色使用這個接口調用不同的算法,一般使用接口或抽象類實現。
具體策略(Concrete Strategy)類:實現了抽象策略定義的接口,提供具體的算法實現。
環境(Context)類:持有一個策略類的引用,最終給客戶端調用。

策略模式的實現

/**
 * 抽象策略類:支付方式
 */
public abstract class Payment {
    public abstract String getName();
    //通用邏輯放到抽象類裏面實現
    public MsgResult pay(String uid, double amount){
        //餘額是否足夠
        if(queryBalance(uid) < amount){
            return new MsgResult(500,"支付失敗","餘額不足");
        }
        return new MsgResult(200,"支付成功","支付金額" + amount);
    }
    //查詢餘額的方法:供子類實現、、假設通過用戶的id查餘額
    protected abstract double queryBalance(String uid);
}
/**
 * 具體策略類:支付寶支付
 */
public class AliPay extends Payment {
    public String getName() {return "支付寶";}
    protected double queryBalance(String uid) {return 900;}
}
/**
 * 具體策略類:京東白條支付
 */
public class JDPay extends Payment {
    public String getName() {return "京東白條";}
    protected double queryBalance(String uid) {return 500;}
}
/**
 * 具體策略類:銀聯支付
 */
public class UnionPay extends Payment {
    public String getName() {return "銀聯支付";}
    protected double queryBalance(String uid) {return 120;}
}
/**
 * 具體策略類:微信支付
 */
public class WechatPay extends Payment {
    public String getName() { return "微信支付";}
    protected double queryBalance(String uid) {return 263;}
}
/**
 * 環境類:也就是策略模式的上下文,供用戶調用
 */
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(){
        return pay(PayStrategy.DEFAULT_PAY);
    }
    //指定支付方式
    public MsgResult pay(String payKey){
        Payment payment = PayStrategy.get(payKey);
        System.out.println("歡迎使用" + payment.getName());
        System.out.println("本次交易金額爲" + amount + ",開始扣款");
        return payment.pay(uid,amount);
    }
}
/**
* 工廠類:存放key和對應的策略類;一般策略模式和工廠模式搭配使用
*/
public class PayStrategy {
    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";
    public static  final String DEFAULT_PAY = ALI_PAY;
    private static Map<String,Payment> strategy = new HashMap<String,Payment>();
    static {
        strategy.put(ALI_PAY,new AliPay());
        strategy.put(JD_PAY,new JDPay());
        strategy.put(WECHAT_PAY,new WechatPay());
        strategy.put(UNION_PAY,new UnionPay());
    }
    public static Payment get(String payKey){
        if(!strategy.containsKey(payKey)){
            return strategy.get(DEFAULT_PAY);
        }
        return strategy.get(payKey);
    }
}
/**
 * 返回的結果集類
 */
public class MsgResult {
    private int code;
    private Object data;
    private String msg;
    public MsgResult(int code, String msg, Object data) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }
    @Override
    public String toString() {
        return "MsgResult{" +
                "code=" + code +
                ", data=" + data +
                ", msg='" + msg + '\'' +
                '}';
    }
}
/**
 * 測試類
 */
public class Test {
    public static void main(String[] args) {
        Order order = new Order("1","2020031401000323",324.5);
        System.out.println(order.pay(PayStrategy.ALI_PAY));
    }
}

程序運行結果如下:

歡迎使用支付寶
本次交易金額爲324.5,開始扣款
MsgResult{code=200, data=支付金額324.5, msg='支付成功'}

在這裏插入圖片描述
舉個反例,如果不使用策略模式代碼會是什麼樣子的?
要寫很多的if-else語句或者switch語句

	if ("AliPay".equals(param)){
	    //支付寶的支付邏輯
	}else if ("JdPay".equals(param)){
	    //京東的支付邏輯
	}else if ("WechatPay".equals(param)){
	    //微信的支付邏輯
	}else if ("UnionPay".equals(param)){
	    //銀聯的支付邏輯
	}else if (...){
	    ...
	}

策略模式的應用場景

策略模式可以解決在有多種算法相似的情況下,使用if-else或switch所帶來的複雜性和臃腫性。在日常業務中,策略模式適用於以下場景:
1.針對同一類型的問題,有多種處理方式,每一種都能獨立解決問題。
2.算法需要自由切換場景
3.需要屏蔽算法規則的場景

策略模式的優缺點

策略模式的主要優點如下:
1.策略模式符合開閉原則
2.避免使用多重條件轉移語句,如if-else或switch
3.使用策略模式可以提高算法的保密性和安全性

其主要缺點如下:
1.客戶端必須知道所有策略算法的區別,以便適時選擇恰當的算法類。
2.策略模式造成很多的策略類。
<<上一篇:模板方法模式
>>下一篇:命令模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章