策略模式在公司项目中的运用实践,看完又可以涨一波实战经验了!
營銷系統是一個動態的、有機地結合的系統,經常會隨著業務的不斷變化發生調整,因此從事這一業務的開發可讓我頭疼了。
之前在工作中就不乏一次遇到過隨意調整營銷策略的情況,在部分場景下由于使用了硬編碼的方式來實現,因此在調整策略的時候顯得特別不靈活。
下邊我列舉一個曾經遇到過的應用場景:
業務部門需要上線一款新型的產品,用戶在線上購買了對應的產品,然后下單支付之后需要享受不同的服務內容,這些服務包含了贈送優惠券,發送紅包補貼,加積分,升級等服務項。并且上線之后,可能會隨著市場的因素的調整,部分服務內容也會有所下架,后期調整因素極高。
下邊是一張用戶建模的圖:
線上買單,到選擇購買的產品類型,再到后續下單之后執行不同的營銷規則,每個產品對應不同的服務項目并且服務項目的內容還可能會隨時調整。
舉個實際案例來說,線上有這么幾款服務產品供消費者選購:
1.999元會員套餐
正常會員服務期1個月
發放5張優惠券
2.1999元會員套餐
正常會員服務期2個月
發放6張優惠券
邀請新人加入app,新人在n天內購買套餐有優惠
3.2999元會員套餐
正常會員服務期3個月
發放7張優惠券
滿2500元消費,返現50元紅包
….
大致看看,不同的產品對應不同的促銷規則,似乎毫無規律可言。
但是如果通過抽象的邏輯將其中的共同部分抽取出來,就會發現其實是有規則可循了。
下邊我給出來一段 “不那么完整的代碼案例” (關于這種營銷手段的設計核心在于思路,沒有完美的代碼,只有不斷精進的設計)
這段代碼主要采用來策略模式的設計思路,不同的產品對應不同的策略,產品和策略之間的關聯可以通過使用數據庫的方式來做綁定。
首先可以將每個服務項目看作是一條營銷的規則手段,因此我定義來一個marketing對象:
/***?營銷對象實體類**?@Author?idea*?@Date?created?in?9:39?上午?2020/5/4*/ @NoArgsConstructor @Data @Builder @AllArgsConstructor public?class?MarketingPO?{/***?主鍵id*/private?Integer?id;/***?營銷手段名稱?存儲class的名稱*/private?String?marketingName;/***?入參?多個可以逗號分割*/private?String?inputVal;/***?描述*/private?String?des;/***?創建時間*/private?Date?createTime;/***?更新時間*/private?Date?updateTime;}接著便是產品和不同營銷手段之間做關聯
/***?通過產品id和營銷手段做關聯**?@Author?idea*?@Date?created?in?3:37?下午?2020/5/4*/ @Data @Builder @AllArgsConstructor @NoArgsConstructor public?class?MarketingProductPO?{/***?主鍵id*/private?Integer?id;/***?營銷工具id*/private?Integer?marketingId;/***?產品編號*/private?String?productNo;/***?描述*/private?String?des;/***?是否有效*/private?Integer?validStatus;/***?創建時間*/private?Date?createTime;/***?更新時間*/private?Date?updateTime; }接著是dao層的部分,不過這里我簡單化地將持久層邏輯寫在來代碼里面,只做參考:
/***?模擬dao層操作**?@Author?idea*?@Date?created?in?10:20?上午?2020/5/4*/ @Repository public?class?MarketingDao?implements?IMarketingDao?{private?static?List<MarketingPO>?MARKETING_LIST?=?new?ArrayList();static?{MarketingPO?disCountMarket?=?MarketingPO.builder().id(1).marketingName("com.sise.idea.present.impl.DiscountStrategy").des("折扣優惠").inputVal("7").build();MarketingPO?redPacketMarket?=?MarketingPO.builder().id(2).marketingName("com.sise.idea.present.impl.RedPacketStrategy").des("紅包優惠").inputVal("8").build();MarketingPO?newMemberCouponMarket?=?MarketingPO.builder().id(3).marketingName("com.sise.idea.present.impl.NewMemberCouponStrategy").des("新人優惠券發送").inputVal("10").build();MARKETING_LIST.add(newMemberCouponMarket);MARKETING_LIST.add(disCountMarket);MARKETING_LIST.add(redPacketMarket);}@Overridepublic?List<MarketingPO>?selectMarketingByIds(List<Integer>?idList)?{List<MarketingPO>?marketingPOS?=?new?ArrayList<>(idList.size());for?(MarketingPO?marketingPO?:?MARKETING_LIST)?{if?(idList.contains(marketingPO.getId()))?{marketingPOS.add(marketingPO);}}return?marketingPOS;} }/***?@Author?idea*?@Date?created?in?3:45?下午?2020/5/4*/ @Repository public?class?MarketingProductDao?implements?IMarketingProductDao?{private?static?List<MarketingProductPO>?MARKET_PRODUCT_LIST?=?new?ArrayList<>();static?{MarketingProductPO?marketingProductPO?=?MarketingProductPO.builder().productNo("p111").marketingId(2).validStatus(1).des("2999套餐-發放優惠券").build();MarketingProductPO?marketingProductPO2?=?MarketingProductPO.builder().productNo("p111").marketingId(3).validStatus(1).des("2999套餐-滿額紅包返現").build();MARKET_PRODUCT_LIST.add(marketingProductPO);MARKET_PRODUCT_LIST.add(marketingProductPO2);}@Overridepublic?List<MarketingProductPO>?selectByProductNo(String?productNo)?{List<MarketingProductPO>?marketingProductPOS?=?new?ArrayList<>();for?(MarketingProductPO?marketingProductPO?:?MARKET_PRODUCT_LIST)?{//產品編碼一致?而且規則有效if(marketingProductPO.getProductNo().equals(productNo)?&&?marketingProductPO.getValidStatus()==1){marketingProductPOS.add(marketingProductPO);}}return?marketingProductPOS;} }接著便是對所有的營銷手段都做了一層統一的封裝和抽象:
package?com.sise.策略模式.present;/***?關于營銷手段的策略**?@Author?idea*?@Date?created?in?9:20?上午?2020/5/4*/ public?interface?IMarketingStrategy?{/***?服務贈送的策略執行**?@param?param?參數*?@return*/boolean?doMarketing(Object?...param); }接下來便是不同的營銷手段對應不同的實現,這里面我簡單做了一些實現:
@Service public?class?RedPacketStrategy?implements?IMarketingStrategy?{@Overridepublic?boolean?doMarketing(Object...?param)?{System.out.println("紅包贈送策略");return?false;} }@Service public?class?DiscountStrategy?implements?IMarketingStrategy?{@Overridepublic?boolean?doMarketing(Object...?param)?{System.out.println("打折優惠");return?false;} }@Service public?class?NewMemberCouponStrategy?implements?IMarketingStrategy?{@Overridepublic?boolean?doMarketing(Object...?param)?{System.out.println("新人贈送策略");return?false;} }@Service public?class?UpgradeStrategy?implements?IMarketingStrategy?{@Overridepublic?boolean?doMarketing(Object...?param)?{System.out.println("升級策略");return?false;} }既然有了不同營銷手段的具體實現方式,那么對于購買不同的產品也需要查詢到不同的營銷手段,這個時候就需要有一個轉換中間者的角色出現了:
/***?營銷工具核心執行器**?@Author?idea*?@Date?created?in?9:34?上午?2020/5/4*/ public?interface?IMarketingCoreService?{/***?執行不同的營銷工具**?@param?productNo?產品編碼*?@return*/boolean?doMarketingJob(String?productNo)?throws?Exception; }/***?營銷工具核心執行器**?@Author?idea*?@Date?created?in?9:34?上午?2020/5/4*/ @Service public?class?MarketingCoreService?implements?IMarketingCoreService?{@Resourceprivate?IMarketingDao?iMarketingDao;@Resourceprivate?IMarketingProductDao?iMarketingProductDao;@Resourceprivate?ApplicationContext?applicationContext;@Overridepublic?boolean?doMarketingJob(String?productNo)?throws?ClassNotFoundException?{System.out.println("doMarketingJob?begin?=============");System.out.println(productNo);List<MarketingProductPO>?marketingProductPOS?=?iMarketingProductDao.selectByProductNo(productNo);if?(marketingProductPOS?!=?null)?{List<Integer>?marketingIdList?=?marketingProductPOS.stream().map(MarketingProductPO::getMarketingId).collect(Collectors.toList());List<MarketingPO>?marketingPOS?=?iMarketingDao.selectMarketingByIds(marketingIdList);for?(MarketingPO?marketingPO?:?marketingPOS)?{String?marketingName?=?marketingPO.getMarketingName();Class<?>?clazz?=?Class.forName(marketingName);IMarketingStrategy?marketingStrategy?=?(IMarketingStrategy)?applicationContext.getBean(clazz);marketingStrategy.doMarketing(marketingPO.getInputVal());}System.out.println("doMarketingJob?end?=============");return?true;}System.out.println("doMarketingJob?setting?is?empty?===========");return?false;} }具體的思路就和策略模式有點類似:
策略模式
模式定義:定義一系列算法,將每個算法都封裝起來,并且它們可以互換。策略模式是一種對象行為模式。
例如下圖:
最后為了方便測試,我在工程里面引入了spring-context的依賴:
測試的入口代碼:
/***?@Author?idea*?@Date?created?in?10:14?上午?2020/5/4*/ public?class?ApplicationDemo?{public?static?void?main(String[]?args)?throws?Exception?{AnnotationConfigApplicationContext?applicationContext?=?new?AnnotationConfigApplicationContext();applicationContext.scan("com.sise.idea.present");//啟動上下文applicationContext.refresh();IMarketingCoreService?marketingCoreService?=?applicationContext.getBean(MarketingCoreService.class);marketingCoreService.doMarketingJob("p111");} }最后根據規則,通過產品編碼來搜索到指定的營銷手段,并執行對應的程序邏輯:
設計不足點
文章上邊我曾經提及過,沒有完美點代碼,只有隨著業務需求不斷變化的設計思路,因此在真正落地整套營銷系統的時候,還需要額外考慮很多的要素。例如說目前的這種設計只能滿足于針對單個產品層面,如果以后有出現針對完整訂單層面(例如說總支付訂單滿xxx元,享受xxx優惠)的還需要額外去思考,加上不同的營銷手段之間是否有出現互斥的場景都是會有可能遇到的情況。
設計模式,是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性。文中我并沒有過多地去講解什么是xx模式,但是當通過某種較為靈活的方式來實現某樣功能時,可能就已經使用了設計模式。
https://gitee.com/IdeaHome_admin/design_pattern/tree/master/design-model/src/main/java/com/sise/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/present
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的策略模式在公司项目中的运用实践,看完又可以涨一波实战经验了!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何抵御频发的 DDOS 攻击?
- 下一篇: NYOJ 594 还是A+B