前言

          最近準(zhǔn)備學(xué)習(xí)下之前項(xiàng)目中用到的設(shè)計(jì)模式,這里代碼都只展示核心業(yè)務(wù)代碼,省略去大多不重要的代碼。

          代碼大多是之前一起工作的小伙伴coding出來(lái)的,我這里做一個(gè)學(xué)習(xí)和總結(jié),我相信技術(shù)能力的提高都是先從模仿開始的,學(xué)習(xí)別人的代碼及設(shè)計(jì)思想也是一種提升的方式。

          ?

          后續(xù)還會(huì)有觀察者模式、責(zé)任鏈模式的博客產(chǎn)出,都是工作中正式運(yùn)用到的場(chǎng)景輸出,希望對(duì)看文章的你也有啟發(fā)和幫助。

          一、業(yè)務(wù)需求
          ?
          我之前做過(guò)在線問(wèn)診的需求,業(yè)務(wù)復(fù)雜,很多節(jié)點(diǎn)需要出發(fā)消息推送,比如用戶下單?需要給醫(yī)生推送短信和push、醫(yī)生接診?需要給用戶發(fā)送短信、push、微信等。產(chǎn)品說(shuō)后期會(huì)有很多不同的節(jié)點(diǎn)觸發(fā)消息發(fā)送。
          ?
          這里就開始抽象需求,首先是發(fā)送消息,很多消息是同樣的策略,只是組裝的數(shù)據(jù)是動(dòng)態(tài)拼接的,所以抽象出:buildSms()、buildPush()、buildWechat()?等構(gòu)造消息體的方法,對(duì)于拼接字段相同的都采用同一策略,列入消息A、B需要通過(guò)醫(yī)生id拼接消息,消息C、D需要通過(guò)用戶id拼接消息,那么A、B就采用同一策略,C、D采用另一策略。
          ? 流程圖大致如下: ? 各個(gè)業(yè)務(wù)系統(tǒng)?根據(jù)策略構(gòu)造自己的消息體,然后通過(guò)kafka發(fā)送個(gè)底層服務(wù),進(jìn)行消息統(tǒng)一推送。 ? ?
          二、策略模式
          ?
          策略模式(Strategy?Pattern)指的是對(duì)象具備某個(gè)行為,但是在不同的場(chǎng)景中,該行為有不同的實(shí)現(xiàn)算法。比如一個(gè)人的交稅比率與他的工資有關(guān),不同的工資水平對(duì)應(yīng)不同的稅率。
          策略模式?使用的就是面向?qū)ο蟮睦^承和多態(tài)機(jī)制,從而實(shí)現(xiàn)同一行為在不同場(chǎng)景下具備不同實(shí)現(xiàn)。 策略模式本質(zhì):分離算法,選擇實(shí)現(xiàn) ?
          主要解決在有多重算法相似的情況下,使用if...else?或者switch...case所帶來(lái)的的復(fù)雜性和臃腫性。 ? ? 代碼示例: 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
          @Override25 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 }} ?
          三、工作中實(shí)際代碼演示
          ? 為了代碼簡(jiǎn)潔和易懂,這里用的都是核心代碼片段,主要看策略使用的方式以及思想即可。 ?
          1、消息枚舉類,這里因?yàn)橄⒊霭l(fā)節(jié)點(diǎn)眾多,所以每一個(gè)節(jié)點(diǎn)都會(huì)對(duì)應(yīng)一個(gè)枚舉類,枚舉中包含短信、push、微信、私信等內(nèi)容。
          1 @Getter 2 public enum MsgCollectEnum { 3 4 /** 5 * 枚舉入口:用戶首次提問(wèn) 給醫(yī)生
          文案內(nèi)容(醫(yī)生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 * 短信文案:用戶首次提問(wèn) 給醫(yī)生
          文案內(nèi)容12 */ 13
          SMS_FIRST_QUESTION_CONTENT(STTurnLinkEnum.DOCTOR_QUESTION_SETTING_PAGE.getStoapp(),
          "您好,有一位用戶向您發(fā)起咨詢,請(qǐng)確認(rèn)接單,趕快進(jìn)入APP查看吧!{0}"); 14 15 16 /** 17 * Push文案:用戶首次提問(wèn) 給醫(yī)生
          文案內(nèi)容18 */ 19
          PUSH_FIRST_QUESTION_CONTENT(STTurnLinkEnum.DOCTOR_QUESTION_SETTING_PAGE.getStoapp(),
          STPushAudioEnum.PAY_SUCCESS.getType(), "您好, 有一位用戶向您發(fā)起了咨詢服務(wù)"); 20 21 22 ......
          23 }
          ?
          ?
          2,消息節(jié)點(diǎn)觸發(fā)代碼
          這里是構(gòu)造上下文MsgContext,主要策略分發(fā)的邏輯在最后一行,這里也會(huì)作為重點(diǎn)來(lái)講解 ? 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,策略分發(fā)

          首先,通過(guò)QuestionMsgStrategy.class?找到對(duì)應(yīng)所有的beanMap,然后通過(guò)自定義注解找到所有對(duì)應(yīng)策略類,最后通過(guò)msgType找到指定的實(shí)現(xiàn)類。接著我們看下策略實(shí)現(xiàn)類
          ? 1 @Slf4j 2 public class STContextStrategyFactory { 3 public <O extends
          STIContext, Textends 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("獲取目標(biāo)代理對(duì)象失敗:{}", e); 19 } 20 log.error("strategy
          type = {} handle is null", type); 21 return null; 22 } 23 } ? ?
          4,策略實(shí)現(xiàn)類
          通過(guò)自定義注解,然后解析msgType值找到指定策略類,通過(guò)不同的策略類構(gòu)造的msg?發(fā)送給kafka。 ? 1 @Component 2
          @STStrategyAnnotation(type = "2101-2104-2113-2016", description = "發(fā)給醫(yī)生,無(wú)其他附屬信息"
          ) 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
          @Override19 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
          * 構(gòu)建短信消息34 * 35 * @param context 36 * @return 37 */ 38 public abstract
          StarSmsIn buildSmsIn(MsgContext context);39 40 41 /** 42 * 構(gòu)建push消息 43 * 44 *
          @param context 45 * @return 46 */ 47 public abstract StarPushIn
          buildPushIn(MsgContext context);48 49 50 /** 51 * 構(gòu)建微信公眾號(hào) 52 * 53 * @param
          context54 * @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 }
          ? ?
          四,策略模式缺點(diǎn)
          整個(gè)消息系統(tǒng)的設(shè)計(jì)起初是基于此策略模式來(lái)實(shí)現(xiàn)的,但是在后續(xù)迭代開發(fā)中會(huì)發(fā)現(xiàn)越來(lái)越不好維護(hù),主要缺點(diǎn)如下:
          a、接入消息推送的研發(fā)同學(xué)需要了解每個(gè)策略類,對(duì)于相同的策略進(jìn)行復(fù)用 b、節(jié)點(diǎn)越來(lái)越多,策略類也越來(lái)越多,系統(tǒng)不易維護(hù)
          c、觸發(fā)節(jié)點(diǎn)枚舉類散落在各個(gè)業(yè)務(wù)系統(tǒng)中,經(jīng)常會(huì)有相同的節(jié)點(diǎn)而不同的msgType ?
          針對(duì)于上述的缺點(diǎn),又重構(gòu)了一把消息系統(tǒng),此次是完全采用節(jié)點(diǎn)配置化方案,提供一個(gè)可視化頁(yè)面進(jìn)行配置,將要構(gòu)造的消息體通過(guò)配置寫入到數(shù)據(jù)庫(kù)中,代碼中通過(guò)不同的占位符進(jìn)行數(shù)據(jù)動(dòng)態(tài)替換。
          這里就不再展示新版系統(tǒng)的代碼了,重構(gòu)后?接入方只需要構(gòu)造msgContext即可,再也不需要自己手動(dòng)去寫不同的策略類了。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問(wèn)題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                久久www免费人成看片好看吗 | 亚洲大几吧色色91视频 | 青青草青娱乐 | 在办公室狂cao丝袜老师h文 | 天天操天天插天天 |