策略(Strategy)模式:又名Policy,它的用意是定義一組算法,把它們一個個封裝起來,並且使他們可以相互替換。策略模式可以獨立於使用他們的客戶端而變化。GOF策略模式靜態結構類圖如下:
通過上圖可以看出策略模式有以下角色構成:
1、抽象策略(Strategy)角色:抽象策略角色由抽象類或接口來承擔,它給出具體策略角色需要實現的接口;
2、具體策略(ConcreteStrategy)角色:實現封裝了具體的算法或行為;
3、場景(Context)角色:持有抽象策略類的引用。
策略模式重點是封裝不同的算法和行為,不同的場景下可以相互替換。策略模式是開閉原則的體現,開閉原則講的是一個軟件實體應該對擴展開放對修改關閉。策略模式在新的策略增加時,不會影響其他類的修改,增加了擴展性,也就是對擴展是開放的;對於場景來說,只依賴於抽象,而不依賴於具體實現,所以對修改是關閉的。策略模式的認識可以借助《java與模式》一書中寫到諸葛亮的錦囊妙計來學習,在不同的場景下趙雲打開不同的錦囊,便化險為夷,錦囊便是抽象策略,具體的錦囊里面的計策便是具體的策略角色,場景就是趙雲,變化的處境
選擇具體策略的條件。
策略模式在程序設計中也很常用,在板橋(banq)的博客里有篇文章叫 “你還在用if else嗎?”
“http://www.jdon.com/artichect/ifelse.htm”講的很好,策略模式不但是繼承的代替方案而且能很好地解決if else問題,下面舉個實例來說明,怎么使用策略模式。
需求如下:
某支付系統接入以下幾種商戶進行充值:易寶網易,快線網銀,19pay手機支付,支付寶支付,駿網一卡通,由於每家充值系統的結算比例不一樣,而且同一家商戶的不同充值方式也有所不同,具體系統情況比較復雜,像支付寶既有支付寶賬號支付和支付寶網銀支付等這些暫時不考慮,為了講述策略模式這里簡單描述,假如分為四種,手機支付,網銀支付,商戶賬號支付和點卡支付。因為沒個支付結算比例不同,所以對手續費低的做一些優惠活動,盡可能讓用戶使用手續費低的支付方式來充值,這樣降低渠道費用,增加收入,具體優惠政策如下:
①網銀充值,8.5折;
②商戶充值,9折;
③手機充值,沒有優惠;
④點卡充值,收取1%的渠道費;
對於一個新手的代碼如下:
package strategy; public class Example { /** * *作者:alaric *時間:2013-8-5上午11:00:06 *描述:計算用戶所付金額 */ public Double calRecharge(Double charge ,RechargeTypeEnum type ){ if(type.equals(RechargeTypeEnum.E_BANK)){ return charge*0.85; }else if(type.equals(RechargeTypeEnum.BUSI_ACCOUNTS)){ return charge*0.90; }else if(type.equals(RechargeTypeEnum.MOBILE)){ return charge; }else if(type.equals(RechargeTypeEnum.CARD_RECHARGE)){ return charge+charge*0.01; }else{ return null; } } }
package strategy; public enum RechargeTypeEnum { E_BANK(1, "網銀"), BUSI_ACCOUNTS(2, "商戶賬號"), MOBILE(3,"手機卡充值"), CARD_RECHARGE(4,"充值卡") ; /** * 狀態值 */ private int value; /** * 類型描述 */ private String description; private RechargeTypeEnum(int value, String description) { this.value = value; this.description = description; } public int value() { return value; } public String description() { return description; } public static RechargeTypeEnum valueOf(int value) { for(RechargeTypeEnum type : RechargeTypeEnum.values()) { if(type.value() == value) { return type; } } return null; } }
可以看出上面四種不同的計算方式在一個方法內部,不利於擴展和維護,當然也不符合面向對象設計原則。對以上的代碼利用策略模式進行修改,類圖如下:
實例代碼如下:
- package strategy.strategy;
- import strategy.RechargeTypeEnum;
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:03:17
- *描述:策略抽象類
- */
- public interface Strategy {
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:05:11
- *描述:策略行為方法
- */
- public Double calRecharge(Double charge ,RechargeTypeEnum type );
- }
- package strategy.strategy;
- import strategy.RechargeTypeEnum;
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:14:23
- *描述:網銀充值
- */
- public class EBankStrategy implements Strategy{
- @Override
- public Double calRecharge(Double charge, RechargeTypeEnum type) {
- return charge*0.85;
- }
- }
- package strategy.strategy;
- import strategy.RechargeTypeEnum;
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:14:08
- *描述:商戶賬號充值
- */
- public class BusiAcctStrategy implements Strategy{
- @Override
- public Double calRecharge(Double charge, RechargeTypeEnum type) {
- // TODO Auto-generated method stub
- return charge*0.90;
- }
- }
- package strategy.strategy;
- import strategy.RechargeTypeEnum;
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:14:43
- *描述:手機充值
- */
- public class MobileStrategy implements Strategy {
- @Override
- public Double calRecharge(Double charge, RechargeTypeEnum type) {
- // TODO Auto-generated method stub
- return charge;
- }
- }
- package strategy.strategy;
- import strategy.RechargeTypeEnum;
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:13:46
- *描述:充值卡充值
- */
- public class CardStrategy implements Strategy{
- @Override
- public Double calRecharge(Double charge, RechargeTypeEnum type) {
- return charge+charge*0.01;
- }
- }
- package strategy.strategy;
- import strategy.RechargeTypeEnum;
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:03:38
- *描述:場景類
- */
- public class Context {
- private Strategy strategy;
- public Double calRecharge(Double charge, Integer type) {
- strategy = StrategyFactory.getInstance().creator(type);
- return strategy.calRecharge(charge, RechargeTypeEnum.valueOf(type));
- }
- public Strategy getStrategy() {
- return strategy;
- }
- public void setStrategy(Strategy strategy) {
- this.strategy = strategy;
- }
- }
- package strategy.strategy;
- import java.util.HashMap;
- import java.util.Map;
- import strategy.RechargeTypeEnum;
- /**
- *
- *作者:alaric
- *時間:2013-8-5上午11:31:12
- *描述:策略工廠 使用單例模式
- */
- public class StrategyFactory {
- private static StrategyFactory factory = new StrategyFactory();
- private StrategyFactory(){
- }
- private static Map<Integer ,Strategy> strategyMap = new HashMap<>();
- static{
- strategyMap.put(RechargeTypeEnum.E_BANK.value(), new EBankStrategy());
- strategyMap.put(RechargeTypeEnum.BUSI_ACCOUNTS.value(), new BusiAcctStrategy());
- strategyMap.put(RechargeTypeEnum.MOBILE.value(), new MobileStrategy());
- strategyMap.put(RechargeTypeEnum.CARD_RECHARGE.value(), new CardStrategy());
- }
- public Strategy creator(Integer type){
- return strategyMap.get(type);
- }
- public static StrategyFactory getInstance(){
- return factory;
- }
- }
- package strategy.strategy;
- import strategy.RechargeTypeEnum;
- public class Client {
- /**
- * 作者:alaric 時間:2013-8-5上午11:33:52 描述:
- */
- public static void main(String[] args) {
- Context context = new Context();
- // 網銀充值100 需要付多少
- Double money = context.calRecharge(100D,
- RechargeTypeEnum.E_BANK.value());
- System.out.println(money);
- // 商戶賬戶充值100 需要付多少
- Double money2 = context.calRecharge(100D,
- RechargeTypeEnum.BUSI_ACCOUNTS.value());
- System.out.println(money2);
- // 手機充值100 需要付多少
- Double money3 = context.calRecharge(100D,
- RechargeTypeEnum.MOBILE.value());
- System.out.println(money3);
- // 充值卡充值100 需要付多少
- Double money4 = context.calRecharge(100D,
- RechargeTypeEnum.CARD_RECHARGE.value());
- System.out.println(money4);
- }
- }
運行結果:
85.0
90.0
100.0
101.0