工作中常見的設計模式-策略模式
阿新 • • 發佈:2019-09-04
前言
最近準備學習下之前專案中用到的設計模式,這裡程式碼都只展示核心業務程式碼,省略去大多不重要的程式碼。
程式碼大多是之前一起工作的小夥伴coding出來的,我這裡做一個學習和總結,我相信技術能力的提高都是先從模仿開始的,學習別人的程式碼及設計思想也是一種提升的方式。
後續還會有觀察者模式、責任鏈模式的部落格產出,都是工作中正式運用到的場景輸出,希望對看文章的你也有啟發和幫助。
一、業務需求
我之前做過線上問診的需求,業務複雜,很多節點需要出發訊息推送,比如使用者下單 需要給醫生推送簡訊和push、醫生接診 需要給使用者傳送簡訊、push、微信等。產品說後期會有很多不同的節點觸發訊息傳送。 這裡就開始抽象需求,首先是傳送訊息,很多訊息是同樣的策略,只是組裝的資料是動態拼接的,所以抽象出:buildSms()、buildPush()、buildWechat() 等構造訊息體的方法,對於拼接欄位相同的都採用同一策略,列入訊息A、B需要通過醫生id拼接訊息,訊息C、D需要通過使用者id拼接訊息,那麼A、B就採用同一策略,C、D採用另一策略。 流程圖大致如下: 各個業務系統 根據策略構造自己的訊息體,然後通過kafka傳送個底層服務,進行訊息統一推送。二、策略模式
1 class Client { 2 public static void main(String[] args) { 3 ICalculator calculator = new Add(); 4 Context context = new Context(calculator); 5 int result = context.calc(1,2); 6 System.out.println(result); 7 } 8 9 10 interface ICalculator { 11 int calc(int a, int b); 12 } 13 14 15 static class Add implements ICalculator { 16 @Override 17 public int calc(int a, int b) { 18 return a + b; 19 } 20 } 21 22 23 static class Sub implements ICalculator { 24 @Override 25 public int calc(int a, int b) { 26 return a - b; 27 } 28 } 29 30 31 static class Multi implements ICalculator { 32 @Override 33 public int calc(int a, int b) { 34 return a * b; 35 } 36 } 37 38 39 static class Divide implements ICalculator { 40 @Override 41 public int calc(int a, int b) { 42 return a / b; 43 } 44 } 45 46 47 static class Context { 48 private ICalculator mCalculator; 49 50 51 public Context(ICalculator calculator) { 52 this.mCalculator = calculator; 53 } 54 55 56 public int calc(int a, int b) { 57 return this.mCalculator.calc(a, b); 58 } 59 }}
三、工作中實際程式碼演示
為了程式碼簡潔和易懂,這裡用的都是核心程式碼片段,主要看策略使用的方式以及思想即可。1、訊息列舉類,這裡因為訊息出發節點眾多,所以每一個節點都會對應一個列舉類,列舉中包含簡訊、push、微信、私信等內容。
1 @Getter 2 public enum MsgCollectEnum { 3 4 /** 5 * 列舉入口:使用者首次提問 給醫生 文案內容(醫生id拼連線) 6 */ 7 FIRST_QUESTION_CONTENT(2101, 1, MsgSmsEnum.SMS_FIRST_QUESTION_CONTENT, MsgPushEnum.PUSH_FIRST_QUESTION_CONTENT, MsgWechatEnum.WECHAT_FIRST_QUESTION_CONTENT); 8 9 10 /** 11 * 簡訊文案:使用者首次提問 給醫生 文案內容 12 */ 13 SMS_FIRST_QUESTION_CONTENT(STTurnLinkEnum.DOCTOR_QUESTION_SETTING_PAGE.getStoapp(), "您好,有一位使用者向您發起諮詢,請確認接單,趕快進入APP檢視吧!{0}"); 14 15 16 /** 17 * Push文案:使用者首次提問 給醫生 文案內容 18 */ 19 PUSH_FIRST_QUESTION_CONTENT(STTurnLinkEnum.DOCTOR_QUESTION_SETTING_PAGE.getStoapp(), STPushAudioEnum.PAY_SUCCESS.getType(), "您好, 有一位使用者向您發起了諮詢服務"); 20 21 22 ...... 23 }
2,訊息節點觸發程式碼
這裡是構造上下文MsgContext,主要策略分發的邏輯在最後一行,這裡也會作為重點來講解1 MsgContext msgContext = new MsgContext(); 2 msgContext.setDoctorId(questionDO.getDoctorId()); 3 msgContext.setReceiveUid(questionDO.getDrUid()); 4 msgContext.setMsgType(MsgCollectEnum.FIRST_QUESTION_CONTENT.getType()); 5 this.stContextStrategyFactory.doStrategy(String.valueOf(msgContext.getMsgType()), QuestionMsgStrategy.class).handleSeniority(msgContext);
3,策略分發
首先,通過QuestionMsgStrategy.class 找到對應所有的beanMap,然後通過自定義註解找到所有對應策略類,最後通過msgType找到指定的實現類。接著我們看下策略實現類1 @Slf4j 2 public class STContextStrategyFactory { 3 public <O extends STIContext, T extends STIContextStrategy<O>> STIContextStrategy<O> doStrategy(String type, Class<T> clazz) { 4 Map<String, T> beanMap = STSpringBeanUtils.getBeanMap(clazz); 5 if (MapUtils.isEmpty(beanMap)) { 6 log.error("獲取class:{} 為空", clazz.getName()); 7 } 8 try { 9 for (Map.Entry<String, T> entry : beanMap.entrySet()) { 10 Object real = STAopTargetUtils.getTarget(entry.getValue()); 11 STStrategyAnnotation annotation = real.getClass().getAnnotation(STStrategyAnnotation.class); 12 List<String> keySet = Splitter.on("-").omitEmptyStrings().trimResults().splitToList(annotation.type()); 13 if (keySet.contains(type)) { 14 return entry.getValue(); 15 } 16 } 17 } catch (Exception e) { 18 log.error("獲取目標代理物件失敗:{}", e); 19 } 20 log.error("strategy type = {} handle is null", type); 21 return null; 22 } 23 }
4,策略實現類
通過自定義註解,然後解析msgType值找到指定策略類,通過不同的策略類構造的msg 傳送給kafka。1 @Component 2 @STStrategyAnnotation(type = "2101-2104-2113-2016", description = "發給醫生,無其他附屬資訊") 3 public class QuestionMsgSimpleToDoctorStrategyImpl extends AbstractQuestionSendMsgStrategy { 4 5 6 @Autowired 7 private RemoteMsgService remoteMsgService; 8 @Autowired 9 private QuestionDetailService questionDetailService; 10 11 12 @Override 13 public StarSmsIn buildSmsIn(MsgContext context) { 14 // do something 15 } 16 17 18 @Override 19 public StarPushIn buildPushIn(MsgContext context) { 20 // do something 21 } 22 23 24 ...... 25 26 27 } 28 29 30 @Slf4j 31 public abstract class AbstractQuestionSendMsgStrategy implements QuestionMsgStrategy { 32 /** 33 * 構建簡訊訊息 34 * 35 * @param context 36 * @return 37 */ 38 public abstract StarSmsIn buildSmsIn(MsgContext context); 39 40 41 /** 42 * 構建push訊息 43 * 44 * @param context 45 * @return 46 */ 47 public abstract StarPushIn buildPushIn(MsgContext context); 48 49 50 /** 51 * 構建微信公眾號 52 * 53 * @param context 54 * @return 55 */ 56 public abstract StarWeChatIn buildWeChatIn(MsgContext context); 57 58 59 @Override 60 public STResultInfo handleSeniority(MsgContext msgContext) { 61 // buildMsg and send kafka 62 } 63 }