<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          策略模式-優(yōu)雅的改造短信業(yè)務模塊

          共 5084字,需瀏覽 11分鐘

           ·

          2022-05-11 02:39

          點擊關注公眾號,Java干貨及時送達??

          來源:www.jianshu.com/p/57a462ea9a6a

          前言

          最近在開發(fā)公司的短信模板功能,簡單的說,就是創(chuàng)建一些包含占位符的短信模板,在發(fā)送短信時將這些占位符使用特定值替換后再發(fā)出,例如短信模板中的公司名稱占位符是{companyName},在發(fā)送時,使用具體的公司名稱將{companyName}替換。

          短信模板是一個獨立的服務,其他模塊在調(diào)用短信發(fā)送接口時,需要指定短信模板code以及要對占位符進行替換的占位符參數(shù);因為調(diào)用短信發(fā)送的業(yè)務場景比較多,如果某次調(diào)用傳入的占位符替換參數(shù)與對應短信模板占位符不匹配,會導致發(fā)出的短信還包含有未替換的占位符,影響到短信發(fā)送的有效性。因此,需要在發(fā)送短信時根據(jù)模板校驗傳入的占位符替換參數(shù)。

          目前定下來的需求是短信模板與傳入的占位符替換參數(shù)必須完全對應才能發(fā)送短信,最簡單的方法就是在發(fā)送短信時加上判斷,如果不滿足條件則拒絕發(fā)送,但是考慮到后續(xù)的拓展性(例如按照業(yè)務場景設定不同的拒絕策略),這一個判斷過程最好是使用策略模式實現(xiàn)。

          策略模式

          在閻宏博士的《JAVA與模式》一書中開頭是這樣描述策略(Strategy)模式的:策略模式屬于對象的行為模式。其用意是針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發(fā)生變化。

          對于從事JAVA開發(fā)的CRUD工程師們而言,實際項目開發(fā)中更多都是寫業(yè)務邏輯,算法可以泛化成各種不同的業(yè)務場景,在同一個業(yè)務場景里,根據(jù)條件的不同需要提供多種不同的業(yè)務處理邏輯,這些業(yè)務處理邏輯的增加或減少是客戶端無需關注的。

          業(yè)務代碼

          本文主要是介紹策略模式,重點就只在于短信發(fā)送時拒絕策略邏輯的處理,不相關的代碼就不介紹了。

          UML類圖

          主要的接口有兩個 SmsTemplatePlaceHolderHandler 短信模板占位符處理器接口,SmsSendRejectStrategy短信發(fā)送拒絕策略接口,SmsTemplatePlaceHolderHandler有一個默認的實現(xiàn)類DefaultSmsTemplatePlaceHolderHandler,其關聯(lián)了一個SmsSendRejectStrategy實例,在發(fā)送短信時,具體的短信發(fā)送拒絕策略實現(xiàn)類將進行具體的發(fā)送拒絕邏輯的處理,如果允許發(fā)送,則由DefaultSmsTemplatePlaceHolderHandler將替換了占位符的短信模板內(nèi)容發(fā)出。

          其中,DefaultSmsTemplatePlaceHolderHandlerSmsSendRejectStrategy的關系就是一個具體的策略模式的體現(xiàn),DefaultSmsTemplatePlaceHolderHandler無需關注拒絕發(fā)送的處理邏輯,調(diào)用SmsSendRejectStrategy實現(xiàn)類的實例進行處理即可。

          DefaultSmsTemplatePlaceHolderHandler
          package?com.cube.share.sms.handler;

          import?com.cube.share.base.utils.JacksonUtils;
          import?com.cube.share.base.utils.PlaceHolderUtils;
          import?com.cube.share.sms.constant.SmsConstant;
          import?com.cube.share.sms.model.param.SmsPlaceHolderParameter;
          import?com.cube.share.sms.strategy.SmsSendRejectStrategy;
          import?com.cube.share.sms.strategy.SmsTemplateContext;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?12:27
          ?*?@description?默認的短信模板占位符處理器
          ?*/

          public?class?DefaultSmsTemplatePlaceHolderHandler?implements?SmsTemplatePlaceHolderHandler?{

          ????private?SmsSendRejectStrategy?rejectStrategy;

          ????public?DefaultSmsTemplatePlaceHolderHandler(SmsSendRejectStrategy?rejectStrategy)?{
          ????????this.rejectStrategy?=?rejectStrategy;
          ????}

          ????@Override
          ????public?String?handle(SmsTemplateContext?templateContext,?SmsPlaceHolderParameter?parameter)?{
          ????????//發(fā)送拒絕策略
          ????????rejectStrategy.reject(templateContext,?parameter);
          ????????return?PlaceHolderUtils.replacePlaceHolder(templateContext.getTemplateContent(),
          ????????????????JacksonUtils.toMap(parameter),
          ????????????????SmsConstant.DEFAULT_PLACE_HOLDER_REGEX,
          ????????????????SmsConstant.DEFAULT_PLACE_HOLDER_KEY_REGEX);
          ????}
          }
          SmsSendRejectStrategy
          package?com.cube.share.sms.strategy;

          import?com.cube.share.base.utils.JacksonUtils;
          import?com.cube.share.sms.model.param.SmsPlaceHolderParameter;
          import?org.springframework.lang.NonNull;

          import?java.util.HashMap;
          import?java.util.Map;
          import?java.util.Set;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?9:49
          ?*?@description?短信發(fā)送的拒絕策略
          ?*/

          public?interface?SmsSendRejectStrategy?{

          ????/**
          ?????*?判斷是否拒絕發(fā)送短信
          ?????*
          ?????*?@param?templateContext?短信模板上下文
          ?????*?@param?parameter???????填充占位符的參數(shù)
          ?????*/

          ????void?reject(SmsTemplateContext?templateContext,?SmsPlaceHolderParameter?parameter);

          ????/**
          ?????*?獲取短信發(fā)送占位符替換參數(shù)Set(不包含value為null)
          ?????*
          ?????*?@param?parameter?填充占位符的參數(shù)
          ?????*?@return?Set
          ?????*/

          ????@NonNull
          ????default?Set?getParameterSet(SmsPlaceHolderParameter?parameter)?{
          ????????Map?parameterMap?=?getParameterMap(parameter);
          ????????return?parameterMap.keySet();
          ????}

          ????/**
          ?????*?獲取短信發(fā)送占位符替換參數(shù)Map(不包含value為null)
          ?????*
          ?????*?@param?parameter?填充占位符的參數(shù)
          ?????*?@return?Map
          ?????*/

          ????@NonNull
          ????default?Map?getParameterMap(SmsPlaceHolderParameter?parameter)?{
          ????????Map?parameterMap?=?JacksonUtils.toMap(parameter);
          ????????Map?filteredParameterMap?=?new?HashMap<>(4);
          ????????if?(parameterMap?!=?null)?{
          ????????????Set>?entrySet?=?parameterMap.entrySet();
          ????????????entrySet.forEach(stringObjectEntry?->?{
          ????????????????if?(stringObjectEntry.getValue()?!=?null)?{
          ????????????????????filteredParameterMap.put(stringObjectEntry.getKey(),?stringObjectEntry.getValue());
          ????????????????}
          ????????????});
          ????????}
          ????????return?filteredParameterMap;
          ????}

          }

          三種拒絕策略的實現(xiàn)類

          package?com.cube.share.sms.strategy;

          import?com.cube.share.sms.model.param.SmsPlaceHolderParameter;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?11:54
          ?*?@description?短信發(fā)送拒絕策略-忽略策略,無論短信發(fā)送入?yún)⑴c模板是否匹配,都允許發(fā)送
          ?*/

          public?class?SmsSendIgnoreStrategy?implements?SmsSendRejectStrategy?{

          ????@Override
          ????public?void?reject(SmsTemplateContext?templateContext,?SmsPlaceHolderParameter?parameter)?{
          ????????//do?nothing
          ????}
          }

          package?com.cube.share.sms.strategy;

          import?com.cube.share.base.templates.CustomException;
          import?com.cube.share.sms.model.param.SmsPlaceHolderParameter;
          import?lombok.extern.slf4j.Slf4j;
          import?org.apache.commons.collections4.CollectionUtils;

          import?java.util.Set;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?11:45
          ?*?@description?SmsSendAnyMatchStrategy,?只要占位符參數(shù)匹配了短信模板中的任意一個占位符key,就允許發(fā)送
          ?*/

          @Slf4j
          public?class?SmsSendAnyMatchStrategy?implements?SmsSendRejectStrategy?{

          ????@Override
          ????public?void?reject(SmsTemplateContext?templateContext,?SmsPlaceHolderParameter?parameter)?{
          ????????Set?parameterKeySet?=?getParameterSet(parameter);
          ????????if?(CollectionUtils.intersection(templateContext.getPlaceHolderKeySet(),?parameterKeySet).size()?<=?0)?{
          ????????????log.error("短信占位符替換參數(shù)與短信模板完全不匹配,templateContent?=?{},parameter?=?{}",?templateContext.getTemplateContent(),?parameter);
          ????????????throw?new?CustomException("短信占位符替換參數(shù)與短信模板完全不匹配");
          ????????}
          ????}
          }

          package?com.cube.share.sms.strategy;

          import?com.cube.share.base.templates.CustomException;
          import?com.cube.share.sms.model.param.SmsPlaceHolderParameter;
          import?lombok.extern.slf4j.Slf4j;

          import?java.util.Set;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?11:57
          ?*?@description?短信發(fā)送拒絕策略-完全匹配,只有當短信入?yún)⑴c短信模板占位符完全匹配時才允許發(fā)送
          ?*/

          @Slf4j
          public?class?SmsSendTotallyMatchStrategy?implements?SmsSendRejectStrategy?{

          ????@Override
          ????public?void?reject(SmsTemplateContext?templateContext,?SmsPlaceHolderParameter?parameter)?{
          ????????Set?parameterKeySet?=?getParameterSet(parameter);
          ????????if?(!parameterKeySet.containsAll(templateContext.getPlaceHolderKeySet()))?{
          ????????????log.error("短信占位符替換參數(shù)與短信模板不完全匹配,templateContent?=?{},parameter?=?{}",?templateContext.getTemplateContent(),?parameter);
          ????????????throw?new?CustomException("短信占位符替換參數(shù)與短信模板不完全匹配");
          ????????}
          ????}
          }
          拒絕策略實例的創(chuàng)建工廠
          package?com.cube.share.sms.factory;

          import?com.cube.share.sms.constant.SmsSendRejectStrategyEnum;
          import?com.cube.share.sms.strategy.SmsSendAnyMatchStrategy;
          import?com.cube.share.sms.strategy.SmsSendIgnoreStrategy;
          import?com.cube.share.sms.strategy.SmsSendRejectStrategy;
          import?com.cube.share.sms.strategy.SmsSendTotallyMatchStrategy;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?12:49
          ?*?@description?拒絕策略工廠
          ?*/

          public?class?SmsSendRejectStrategyFactory?{

          ????private?static?final?SmsSendIgnoreStrategy?IGNORE_STRATEGY?=?new?SmsSendIgnoreStrategy();

          ????private?static?final?SmsSendAnyMatchStrategy?ANY_MATCH_STRATEGY?=?new?SmsSendAnyMatchStrategy();

          ????private?static?final?SmsSendTotallyMatchStrategy?TOTALLY_MATCH_STRATEGY?=?new?SmsSendTotallyMatchStrategy();

          ????public?static?SmsSendRejectStrategy?getStrategy(SmsSendRejectStrategyEnum?strategyEnum)?{
          ????????switch?(strategyEnum)?{
          ????????????case?IGNORE:
          ????????????????return?IGNORE_STRATEGY;
          ????????????case?ANY_MATCH:
          ????????????????return?ANY_MATCH_STRATEGY;
          ????????????case?TOTALLY_MATCH:
          ????????????????return?TOTALLY_MATCH_STRATEGY;
          ????????????default:
          ????????????????throw?new?IllegalArgumentException("Illegal?StrategyEnum?Param");
          ????????}
          ????}

          }
          短信發(fā)送服務
          package?com.cube.share.sms.service;

          import?com.cube.share.base.templates.CustomException;
          import?com.cube.share.sms.config.SmsConfig;
          import?com.cube.share.sms.constant.SmsSendRejectStrategyEnum;
          import?com.cube.share.sms.factory.SmsSendRejectStrategyFactory;
          import?com.cube.share.sms.handler.DefaultSmsTemplatePlaceHolderHandler;
          import?com.cube.share.sms.handler.SmsTemplatePlaceHolderHandler;
          import?com.cube.share.sms.model.param.SmsSendParam;
          import?com.cube.share.sms.strategy.SmsTemplateContext;
          import?lombok.extern.slf4j.Slf4j;
          import?org.springframework.stereotype.Service;

          import?javax.annotation.Resource;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?9:03
          ?*?@description?短信服務
          ?*/

          @Service
          @Slf4j
          public?class?SmsService?{

          ????@Resource
          ????private?SmsConfig?smsConfig;

          ????private?SmsTemplatePlaceHolderHandler?placeHolderHandler?=
          ????????????new?DefaultSmsTemplatePlaceHolderHandler(SmsSendRejectStrategyFactory.getStrategy(SmsSendRejectStrategyEnum.ANY_MATCH));

          ????public?void?send(SmsSendParam?param)?{
          ????????String?templateContent?=?smsConfig.getTemplates().get(param.getTemplateCode());
          ????????if?(templateContent?==?null)?{
          ????????????throw?new?CustomException("不正確的短信模板");
          ????????}
          ????????SmsTemplateContext?templateContext?=?SmsTemplateContext.from(templateContent,?param.getTemplateCode());
          ????????String?sendContent?=?placeHolderHandler.handle(templateContext,?param.getParameter());
          ????????log.info("短信發(fā)送:?{}",?sendContent);
          ????}
          }

          測試

          短信模板在配置文件中

          #短信
          sms:
          ??#模板
          ??templates:
          ????1:?"尊敬的用戶您好,{companyName}定于{address}開展主題為{title}的營銷活動,活動時間{startTime}-{endTime},歡迎您的光臨!"
          ????2:?"尊敬的用戶您好,{address}開展主題為{title}的營銷活動將于明天開始,歡迎您的光臨!"

          單元測試類

          package?com.cube.share.sms.service;

          import?com.cube.share.sms.model.param.SmsPlaceHolderParameter;
          import?com.cube.share.sms.model.param.SmsSendParam;
          import?org.junit.jupiter.api.Test;
          import?org.springframework.boot.test.context.SpringBootTest;

          import?javax.annotation.Resource;

          /**
          ?*?@author?cube.li
          ?*?@date?2021/9/4?12:00
          ?*?@description
          ?*/

          @SpringBootTest
          class?SmsServiceTest?{

          ????@Resource
          ????SmsService?smsService;

          ????@Test
          ????void?send()?{
          ????????SmsSendParam?smsSendParam?=?new?SmsSendParam();
          ????????smsSendParam.setTemplateCode(1);
          ????????SmsPlaceHolderParameter?placeHolderParameter?=?new?SmsPlaceHolderParameter();
          ????????placeHolderParameter.setAddress("上海");
          ????????smsSendParam.setParameter(placeHolderParameter);
          ????????smsService.send(smsSendParam);
          ????}

          }

          更改拒絕策略,發(fā)送短信時日志如下:

          • SmsSendAnyMatchStrategy
          2021-09-04?14:34:36.261??INFO?5528?---?[???????????main]?com.cube.share.sms.service.SmsService????:?短信發(fā)送:?尊敬的用戶您好,{companyName}定于上海開展主題為{title}的營銷活動,活動時間{startTime}-{endTime},歡迎您的光臨!

          可以看出,當拒絕策略為SmsSendAnyMatchStrategy時,只要占位符入?yún)⑴c短信模板中的占位符有一個匹配,就能夠發(fā)送成功

          • SmsSendTotallyMatchStrategy

          占位符參數(shù)與模板占位符不完全匹配時發(fā)送失敗

          不完全匹配.png
          2021-09-04?14:38:16.133?ERROR?3896?---?[???????????main]?c.c.s.s.s.SmsSendTotallyMatchStrategy????:?短信占位符替換參數(shù)與短信模板不完全匹配,templateContent?=?尊敬的用戶您好,{companyName}定于{address}開展主題為{title}的營銷活動,活動時間{startTime}-{endTime},歡迎您的光臨!,parameter?=?SmsPlaceHolderParameter(companyName=null,?title=null,?startTime=null,?endTime=null,?address=上海,?url=null)
          com.cube.share.base.templates.CustomException:?短信占位符替換參數(shù)與短信模板不完全匹配

          ????at?com.cube.share.sms.strategy.SmsSendTotallyMatchStrategy.reject(SmsSendTotallyMatchStrategy.java:22)

          占位符參數(shù)與模板占位符完全匹配時發(fā)送成功

          完全匹配.png

          代碼示例:

          https://gitee.com/li-cube/share/tree/master/sms

          總結(jié)

          業(yè)務邏輯說到底就是if-else,使用設計模式能夠使代碼更易維護、更易拓展,并且代碼的閱讀性更強;雖然不使用設計模式照樣能夠?qū)崿F(xiàn)業(yè)務,不過就是多套幾層if-else而已,但是人活著總歸要有點追求,只有做到不止于業(yè)務、不止于代碼,才能成為一個脫離低級CRUD的程序員。

          1.?實體映射最強工具類:MapStruct 真香!

          2.?同事多線程使用不當導致OOM,被我懟了一頓

          3.?你見過哪些目瞪口呆的 Java 代碼技巧?

          4.?Typora 保姆級教程

          最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點“在看”,關注公眾號并回復?Java?領取,更多內(nèi)容陸續(xù)奉上。

          PS:因公眾號平臺更改了推送規(guī)則,如果不想錯過內(nèi)容,記得讀完點一下在看,加個星標,這樣每次新文章推送才會第一時間出現(xiàn)在你的訂閱列表里。

          “在看”支持小哈呀,謝謝啦??

          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产免费高清AV影视 | 欧美日韩性色情AⅤ在线一级二级 | 俺去啦在线观看 | 欧美不卡a | a 在线资源 |