求心丶 发表于 2021-10-19 23:16:42

策略模式-短信模板业务场景

前言

最近在开发公司的短信模板功能,简单的说,就是创建一些包含占位符的短信模板,在发送短信时将这些占位符使用特定值替换后再发出,例如短信模板中的公司名称占位符是{companyName},在发送时,使用具体的公司名称将{companyName}替换。
短信模板是一个独立的服务,其他模块在调用短信发送接口时,需要指定短信模板code以及要对占位符进行替换的占位符参数;因为调用短信发送的业务场景比较多,如果某次调用传入的占位符替换参数与对应短信模板占位符不匹配,会导致发出的短信还包含有未替换的占位符,影响到短信发送的有效性。因此,需要在发送短信时根据模板校验传入的占位符替换参数。
目前定下来的需求是短信模板与传入的占位符替换参数必须完全对应才能发送短信,最简单的方法就是在发送短信时加上判断,如果不满足条件则拒绝发送,但是考虑到后续的拓展性(例如按照业务场景设定不同的拒绝策略),这一个判断过程最好是使用策略模式实现。
策略模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述策略(Strategy)模式的:策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
对于从事JAVA开发的CRUD工程师们而言,实际项目开发中更多都是写业务逻辑,算法可以泛化成各种不同的业务场景,在同一个业务场景里,根据条件的不同需要提供多种不同的业务处理逻辑,这些业务处理逻辑的增加或减少是客户端无需关注的。
业务代码

本文主要是介绍策略模式,重点就只在于短信发送时拒绝策略逻辑的处理,不相关的代码就不介绍了。


主要的接口有两个 SmsTemplatePlaceHolderHandler 短信模板占位符处理器接口,SmsSendRejectStrategy短信发送拒绝策略接口,SmsTemplatePlaceHolderHandler有一个默认的实现类DefaultSmsTemplatePlaceHolderHandler,其关联了一个SmsSendRejectStrategy实例,在发送短信时,具体的短信发送拒绝策略实现类将进行具体的发送拒绝逻辑的处理,如果允许发送,则由DefaultSmsTemplatePlaceHolderHandler将替换了占位符的短信模板内容发出。
其中,DefaultSmsTemplatePlaceHolderHandler与SmsSendRejectStrategy的关系就是一个具体的策略模式的体现,DefaultSmsTemplatePlaceHolderHandler无需关注拒绝发送的处理逻辑,调用SmsSendRejectStrategy实现类的实例进行处理即可。
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) {      //发送拒绝策略      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 短信发送的拒绝策略 */public interface SmsSendRejectStrategy {    /**   * 判断是否拒绝发送短信   *   * @param templateContext 短信模板上下文   * @param parameter       填充占位符的参数   */    void reject(SmsTemplateContext templateContext, SmsPlaceHolderParameter parameter);    /**   * 获取短信发送占位符替换参数Set(不包含value为null)   *   * @param parameter 填充占位符的参数   * @return Set   */    @NonNull    default Set getParameterSet(SmsPlaceHolderParameter parameter) {      Map parameterMap = getParameterMap(parameter);      return parameterMap.keySet();    }    /**   * 获取短信发送占位符替换参数Map(不包含value为null)   *   * @param parameter 填充占位符的参数   * @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;    }}<hr>三种拒绝策略的实现类
package com.cube.share.sms.strategy;import com.cube.share.sms.model.param.SmsPlaceHolderParameter;/** * @author cube.li * @date 2021/9/4 11:54 * @description 短信发送拒绝策略-忽略策略,无论短信发送入参与模板是否匹配,都允许发送 */public class SmsSendIgnoreStrategy implements SmsSendRejectStrategy {    @Override    public void reject(SmsTemplateContext templateContext, SmsPlaceHolderParameter parameter) {      //do nothing    }}<hr>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, 只要占位符参数匹配了短信模板中的任意一个占位符key,就允许发送 */@Slf4jpublic class SmsSendAnyMatchStrategy implements SmsSendRejectStrategy {    @Override    public void reject(SmsTemplateContext templateContext, SmsPlaceHolderParameter parameter) {      Set parameterKeySet = getParameterSet(parameter);      if (CollectionUtils.intersection(templateContext.getPlaceHolderKeySet(), parameterKeySet).size()
页: [1]
查看完整版本: 策略模式-短信模板业务场景