概述
策略模式
策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。对于从事JAVA开发的CRUD工程师们而言,实际项目开发中更多都是写业务逻辑,算法可以泛化成各种不同的业务场景,在同一个业务场景里,根据条件的不同需要提供多种不同的业务处理逻辑,这些业务处理逻辑的增加或减少是客户端无需关注的。
业务代码
本文主要是介绍策略模式,重点就只在于短信发送时拒绝策略逻辑的处理,不相关的代码就不介绍了。
UML类图
主要的接口有两个 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<String> getParameterSet(SmsPlaceHolderParameter parameter) {
Map<String, Object> parameterMap = getParameterMap(parameter);
return parameterMap.keySet();
}
/**
* 获取短信发送占位符替换参数Map(不包含value为null)
*
* @param parameter 填充占位符的参数
* @return Map
*/
@NonNull
default Map<String, Object> getParameterMap(SmsPlaceHolderParameter parameter) {
Map<String, Object> parameterMap = JacksonUtils.toMap(parameter);
Map<String, Object> filteredParameterMap = new HashMap<>(4);
if (parameterMap != null) {
Set<Map.Entry<String, Object>> entrySet = parameterMap.entrySet();
entrySet.forEach(stringObjectEntry -> {
if (stringObjectEntry.getValue() != null) {
filteredParameterMap.put(stringObjectEntry.getKey(), stringObjectEntry.getValue());
}
});
}
return filteredParameterMap;
}
}
三种拒绝策略的实现类
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
}
}
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,就允许发送
*/
@Slf4j
public class SmsSendAnyMatchStrategy implements SmsSendRejectStrategy {
@Override
public void reject(SmsTemplateContext templateContext, SmsPlaceHolderParameter parameter) {
Set<String> parameterKeySet = getParameterSet(parameter);
if (CollectionUtils.intersection(templateContext.getPlaceHolderKeySet(), parameterKeySet).size() <= 0) {
log.error("短信占位符替换参数与短信模板完全不匹配,templateContent = {},parameter = {}", templateContext.getTemplateContent(), parameter);
throw new CustomException("短信占位符替换参数与短信模板完全不匹配");
}
}
}
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 短信发送拒绝策略-完全匹配,只有当短信入参与短信模板占位符完全匹配时才允许发送
*/
@Slf4j
public class SmsSendTotallyMatchStrategy implements SmsSendRejectStrategy {
@Override
public void reject(SmsTemplateContext templateContext, SmsPlaceHolderParameter parameter) {
Set<String> parameterKeySet = getParameterSet(parameter);
if (!parameterKeySet.containsAll(templateContext.getPlaceHolderKeySet())) {
log.error("短信占位符替换参数与短信模板不完全匹配,templateContent = {},parameter = {}", templateContext.getTemplateContent(), parameter);
throw new CustomException("短信占位符替换参数与短信模板不完全匹配");
}
}
}
拒绝策略实例的创建工厂
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");
}
}
}
短信发送服务
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("短信发送: {}", 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);
}
}
更改拒绝策略,发送短信时日志如下:
-
SmsSendAnyMatchStrategy
2021-09-04 14:34:36.261 INFO 5528 --- [ main] com.cube.share.sms.service.SmsService : 短信发送: 尊敬的用户您好,{companyName}定于上海开展主题为{title}的营销活动,活动时间{startTime}-{endTime},欢迎您的光临!
可以看出,当拒绝策略为SmsSendAnyMatchStrategy
时,只要占位符入参与短信模板中的占位符有一个匹配,就能够发送成功
-
SmsSendTotallyMatchStrategy
占位符参数与模板占位符不完全匹配时发送失败
不完全匹配.png
2021-09-04 14:38:16.133 ERROR 3896 --- [ main] c.c.s.s.s.SmsSendTotallyMatchStrategy : 短信占位符替换参数与短信模板不完全匹配,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: 短信占位符替换参数与短信模板不完全匹配
at com.cube.share.sms.strategy.SmsSendTotallyMatchStrategy.reject(SmsSendTotallyMatchStrategy.java:22)
占位符参数与模板占位符完全匹配时发送成功
最后
以上就是机灵舞蹈为你收集整理的策略模式-改造短信业务模块的全部内容,希望文章能够帮你解决策略模式-改造短信业务模块所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复