策略模式是一種行爲型模式,它將對象和行爲分開,將行爲定義爲 一個行爲接口
和 具體行爲的實現
。策略模式最大的特色是行爲的變化,行爲之間能夠相互替換。java
每一個if判斷均可以理解爲就是一個策略。git
策略模式把對象自己和行爲區分開來,所以咱們整個模式也分爲三個部分。github
一、抽象策略類(Strategy):策略的抽象,行爲方式的抽象 二、具體策略類(ConcreteStrategy):具體的策略實現,每一種行爲方式的具體實現。 三、環境類(Context):用來封裝具體行爲,操做策略的上下文環境。
這裏舉個簡單的例子,來理解開發中運用策略模式的場景。算法
有一個打車軟件,如今有三種計費模式給 用戶 選擇,一、拼車 二、快車 三、豪車這個時候用戶就是對象,三種計費方式就是行爲,能夠根據不一樣的行爲計算不一樣不通的值。spring
1)傳統實現方式數據庫
代碼
segmentfault
/** * @Description: 這裏只展現計費最終費用示例 * * @param type 計費類型 * @param originalPrice 原始價格 */ public Double calculationPrice(String type, Double originalPrice) { //拼車計費 if (type.equals("pc")) { return originalPrice * 0.5; } //快車計費 if (type.equals("kc")) { return originalPrice * 1; } //豪車計費 if (type.equals("hc")) { return originalPrice * 2; } return originalPrice; }
傳統的實現方式,經過傳統if代碼判斷。這樣就會致使後期的維護性很是差。當後期須要新增計費方式,還須要在這裏再加上if(),也不符合設計模式的開閉原則。設計模式
2)策略模式實現微信
抽象策略類
架構
/** * 出行策略接口 */ public interface PriceStrategy { /** * @param originalPrice 原始價格 * @return 計算後的價格 */ Double countPrice(Double originalPrice); }
具體策略實現類
/** * @Description: 拼車的計費方式 */ public class PcStrategy implements PriceStrategy { @Override public Double countPrice(Double originalPrice) { return originalPrice * 0.5; } } /** * @Description: 快車的計費方式 */ public class KcStrategy implements PriceStrategy { @Override public Double countPrice(Double originalPrice) { return originalPrice * 1; } } /** * @Description: 拼車的計費方式 */ public class HcStrategy implements PriceStrategy { @Override public Double countPrice(Double originalPrice) { return originalPrice * 2; } }
環境類
也叫作上下文類或環境類,起承上啓下封裝做用。
/** * 負責和具體的策略類交互 * 這樣的話,具體的算法和直接的客戶端調用分離了,使得算法能夠獨立於客戶端獨立的變化。 * 若是使用spring的依賴注入功能,還能夠經過配置文件,動態的注入不一樣策略對象,動態的切換不一樣的算法. */ public class PriceContext { /** * 出行策略接口 */ private PriceStrategy riceStrategy; /** * 構造函數注入 */ public PriceContext(PriceStrategy riceStrategy) { this.riceStrategy = riceStrategy; } /** * 計算價格 */ public Double countPrice(Double originalPrice) { return riceStrategy.countPrice(originalPrice); } }
測試類
public static void main(String[] args) { //具體行爲策略 PriceStrategy pcStrategy = new PcStrategy(); PriceStrategy kcStrategy = new KcStrategy(); PriceStrategy hcStrategy = new HcStrategy(); //用戶選擇不一樣的策略 PriceContext pcContext = new PriceContext(pcStrategy); PriceContext kcContext = new PriceContext(kcStrategy); PriceContext hcContext = new PriceContext(hcStrategy); System.out.println("拼車價格 = " + pcContext.countPrice(10D)); System.out.println("快車價格 = " + kcContext.countPrice(10D)); System.out.println("豪車價格 = " + hcContext.countPrice(10D)); }
運行結果
拼車價格 = 5.0 快車價格 = 10.0 豪車價格 = 20.0
整理流程就是這個樣的,這裏有一點須要注意 我在作測試的時候具體策略對象都是new出來,而實際運用應該經過spring來管理,這樣才能作到真正的開閉原則。下面會在舉例。
優勢
1)避免使用多重條件判斷
若是沒有策略模式,一個策略家族有多個策略算法,一會要使用A策略,一會要使用B策略,怎麼設計呢?使用多重if的條件語句?多重條件語句不易維護,並且出錯的機率大大加強。
使用策略模式後,簡化了操做,同時避免了條件語句判斷。
2)擴展性良好
在現有的系統中增長一個策略太容易了,只要實現接口就能夠了,其餘都不用修改,相似於一個可反覆拆卸的插件,這大大地符合了OCP原則。
缺點
1)策略類數量增多
策略模式一個明顯的缺點是當備用行爲過多時,行爲對象會很是龐大
經過上面的優缺點咱們能夠很好的去思考,什麼場景下能夠考慮用策略模式?
個人理解就是:每一個if判斷均可以理解爲就是一個策略。按理說均可以採用策略模式,可是若是if else裏面的邏輯很少,且複用性很低,那就不須要。若是if裏面的行爲比較大
並且這些行爲複用性比較高就能夠考慮經過採用策略模式。
在咱們生活中比較常見的應用模式有:
一、電商網站支付方式,通常分爲銀聯、微信、支付寶,能夠採用策略模式 二、電商網站活動方式,通常分爲滿減送、限時折扣、包郵活動,拼團等能夠採用策略模式
最近正在作到電商項目,由於有多個活動,因此我就考慮用策略模式來實現。咱們活動分爲不少種滿減送,包郵活動,限時折扣等等,這裏大體寫下對於多個活動如何去使用策略模式。
活動是跟訂單綁定在一塊兒的,只有下了單纔去計算這個訂單走了哪一個活動。
/** * @Description: 訂單實體 */ public class Order { /** * 用戶ID */ private Long userId; /** * 訂單編號 */ private String orderNumber; /** * 購買數量 */ private Integer goodsNumber; /** * 訂單運費 */ private Double orderFreight; /** * 訂單總價(訂單價格 + 運費) */ private Double orderPrice; /** * 活動類型 一、包郵 二、滿減送 三、限時折扣 */ private String activityType; /** * 活動ID */ private String activityId; //省略get set方法
/** * 定義一個總的活動抽象 */ public interface ActivityStrategy { /** * 定義一個咱們優惠活動的價格算法方法 */ Order calculate (Order order); }
FreeShippingActivity
包郵
/** * @Description: 包郵活動 */ @Component @Service("freeShippingActivity") public class FreeShippingActivity implements ActivityStrategy { @Override public Order calculate(Order order) { //包郵活動是一個大的主題 ,裏面能夠建立不少小活動 好比價格滿100包郵活動,或者滿2件以上包郵活動,江浙滬包郵活動等等 //若是這裏經過活動ID獲取用戶具體選擇了哪個活動。 String activityId = order.getActivityId(); //查詢數據庫 System.out.println("模擬查詢數據庫 ,當前的活動是滿100包郵"); order.setOrderFreight(0.0D); return order; } }
FullDeliveryActivity
(滿減送活動)
/** * @Description: 滿減送活動 */ @Component @Service("fullDeliveryActivity") public class FullDeliveryActivity implements ActivityStrategy { @Override public Order calculate(Order order) { //若是這裏經過活動ID獲取用戶具體選擇了哪個活動。 String activityId = order.getActivityId(); //查詢數據庫 System.out.println("模擬查詢數據庫 ,當前的活動是滿100減20的"); if (order.getOrderPrice() > 100) { order.setOrderPrice(order.getOrderPrice() - 20); } return order; } }
LimitDiscountActivity
(限時折扣活動)
/** * @Description: 限時折扣活動 */ @Component @Service("limitDiscountActivity") public class LimitDiscountActivity implements ActivityStrategy { @Override public Order calculate(Order order) { //若是這裏經過活動ID獲取用戶具體選擇了哪個活動。 String activityId = order.getActivityId(); //查詢數據庫 System.out.println("模擬查詢數據庫 ,當前的活動是截至2020.10.1前 打9折"); order.setOrderPrice(order.getOrderPrice() * 0.9); return order; } }
/** * 負責和具體的策略類交互 動態的切換不一樣的算法 */ @Component public class ActivityContext { @Autowired private FreeShippingActivity freeShippingActivity; @Autowired FullDeliveryActivity fullDeliveryActivity; @Autowired LimitDiscountActivity limitDiscountActivity; /** * 出行策略接口 */ private final Map<String, ActivityStrategy> activityStrategyMap = new HashMap(); /** * 初始化 把這幾個活動的示例 初始化的時候就裝到一個map集合中 */ @PostConstruct public void init() { //一、包郵 二、滿減送 三、限時折扣 activityStrategyMap.put("1", freeShippingActivity); activityStrategyMap.put("2", fullDeliveryActivity); activityStrategyMap.put("3", limitDiscountActivity); } /** * 計算價格 */ public Order calculate(Order order) { ActivityStrategy activityStrategy = activityStrategyMap.get(order.getActivityType()); return activityStrategy.calculate(order); } }
@RestController public class PayController { @Autowired private ActivityContext activityContext; @RequestMapping("/test") public Object test(){ Order order = new Order(); //1 表明包郵 order.setActivityType("1"); //具體活動ID order.setActivityId("12"); //總價 order.setOrderPrice(200D); //運費 order.setOrderFreight(10D); return activityContext.calculate(order); } } //輸出: 模擬查詢數據庫 ,當前的活動是包郵
總結
這裏咱們沒有用到if else來判斷用戶到底選擇了哪一個活動類型,而是經過先把全部活動的bean實體裝入一個map中,而後經過activityType 來獲取具體是哪一個活動類型。
之後新添加一個活動,好比拼團活動,咱們只需作兩步
一、新建一個拼團活動策略類 實現總策略接口 二、activityStrategyMap中put這個折扣活動的bean實體。
除了map管理bean外,還有一種方式就是能夠經過spring來管理這些具體的策略類
//freeShippingActivity 這個值能夠在數據庫添加一張表來管理,而後來讀取 這樣的話新建活動只須要作上面的第一步,代碼更加好維護了。 ActivityStrategy payStrategy= SpringUtils.getBean("freeShippingActivity", ActivityStrategy.class);
這裏只是列了個架構,實際開發中比這個複雜多了,由於能夠同時選擇多個活動,活動於活動之間又會有互斥關係。
一、策略模式
別人罵我胖,我會生氣,由於我內心認可了我胖。別人說我矮,我就會以爲可笑,由於我內心知道我不可能矮。這就是咱們爲何會對別人的攻擊生氣。 攻我盾者,乃我心裏之矛(8)。