【设计模式】使用工厂,策略,模板设计模式,实现规则校验功能
前言
频次、频率、白名单、敏感词等,都是用于支撑核心业务之外辅助流程,这些流程都是比较容易随着业务的变动而发生变化。所以我们要把这类东西设计在核心流程之外,而不能直接把规则的代码与核心业务的代码写在一块。因为区分不出边界的代码,会让工程的腐化程度不断加剧。
实现
本文示例为实现一个敏感词校验和使用次数校验
1.使用策略模式定义各种校验规则
定义规则统一接口ILogicFilter,所有的校验规则都需要实现fileter方法
public interface ILogicFilter { RuleLogicEntity<ChatProcessAggregate> fileter(ChatProcessAggregate chatProcessAggregate) throws Exception;}
定义具体的校验规则(这里使用到的缓存为谷歌guava库提供的缓存)
访问次数校验规则
解释
/** * @author www.luckysj.top 刘仕杰 * @description 访问次数校验规则 * @create 2023/12/08 17:02:36 */@Slf4j@Component@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.ACCESS_LIMIT)public class AccessLimitFilter implements ILogicFilter { // 访问频次限制 @Value("${app.config.limit-count}") private Integer limitCount; // 访问白名单 @Value("${app.config.white-list}") private String whiteListStr; // 访问次数缓存 @Resource private Cache<String, Integer> visitCache; @Override public RuleLogicEntity<ChatProcessAggregate> fileter(ChatProcessAggregate chatProcess) throws Exception { // 白名单用户不做限制 if(chatProcess.isWhileList(whiteListStr)){ return RuleLogicEntity.<ChatProcessAggregate>builder() .type(LogicCheckTypeVO.SUCCESS).data(chatProcess).build(); } String openid = chatProcess.getOpenid(); // 判断访问次数 int visitCount = visitCache.get(openid, () -> 0); if (visitCount < limitCount) { visitCache.put(openid, visitCount + 1); return RuleLogicEntity.<ChatProcessAggregate>builder() .type(LogicCheckTypeVO.SUCCESS).data(chatProcess).build(); } return RuleLogicEntity.<ChatProcessAggregate>builder() .info("您今日的免费" + limitCount + "次,已耗尽!") .type(LogicCheckTypeVO.REFUSE).data(chatProcess).build(); }}
敏感词校验规则
解释
/** * @author www.luckysj.top 刘仕杰 * @description 敏感词校验规则 * @create 2023/12/08 17:02:36 */@Slf4j@Component@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.SENSITIVE_WORD)public class SensitiveWordFilter implements ILogicFilter { // 敏感词的引导类 @Resource private SensitiveWordBs words; // 访问白名单 @Value("${app.config.white-list}") private String whiteListStr; @Override public RuleLogicEntity<ChatProcessAggregate> fileter(ChatProcessAggregate chatProcess) throws Exception { // 白名单用户不做限制 if(chatProcess.isWhileList(whiteListStr)){ return RuleLogicEntity.<ChatProcessAggregate>builder() .type(LogicCheckTypeVO.SUCCESS).data(chatProcess).build(); } ChatProcessAggregate newChatProcessAggregate = new ChatProcessAggregate(); newChatProcessAggregate.setOpenid(chatProcess.getOpenid()); newChatProcessAggregate.setModel(chatProcess.getModel()); List<MessageEntity> newMessages = chatProcess.getMessages().stream() .map(message -> { String content = message.getContent(); // 替换敏感词 String replace = words.replace(content); return MessageEntity.builder() .role(message.getRole()) .name(message.getName()) .content(replace) .build(); }) .collect(Collectors.toList()); newChatProcessAggregate.setMessages(newMessages); return RuleLogicEntity.<ChatProcessAggregate>builder() .type(LogicCheckTypeVO.SUCCESS) .data(newChatProcessAggregate) .build(); }}
定义注解LogicStrategy
解释
@Target({ElementType.TYPE}) //指定注解可以应用的地方,ElementType.TYPE 表示该注解可以应用在类、接口(包括注解类型)或枚举声明@Retention(RetentionPolicy.RUNTIME) //指定注解的生命周期,即它在什么时候可用,RetentionPolicy.RUNTIME 表示在运行时可用public @interface LogicStrategy { DefaultLogicFactory.LogicModel logicMode();}
2.使用工厂模式编写校验规则工厂
解释
/** * @author www.luckysj.top 刘仕杰 * @description 校验规则工厂 * @create 2023/12/07 19:48:27 */@Servicepublic class DefaultLogicFactory { // 过滤规则 private Map<String, ILogicFilter> logicFilterMap = new ConcurrentHashMap<>(); public DefaultLogicFactory(List<ILogicFilter> logicFilters) { logicFilters.forEach(logic -> { // 根据类和注解名找到校验规则上的注解 LogicStrategy strategy = AnnotationUtils.findAnnotation(logic.getClass(), LogicStrategy.class); if (null != strategy) { // 以注解值为key,校验规则为logic保存到logicFilterMap中 logicFilterMap.put(strategy.logicMode().getCode(), logic); } }); } // 获取校验规则集 public Map<String, ILogicFilter> openLogicFilter() { return logicFilterMap; } /** * 规则逻辑枚举 */ public enum LogicModel { ACCESS_LIMIT("ACCESS_LIMIT", "访问次数过滤"), SENSITIVE_WORD("SENSITIVE_WORD", "敏感词过滤"), ; private String code; private String info; LogicModel(String code, String info) { this.code = code; this.info = info; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } }}
3.在service层使用模板模式
定义模板抽象类类
解释
/** * @author www.luckysj.top 刘仕杰 * @description xxx服务模板模式抽象类 * @create 2023/12/04 19:50:27 */@Slf4jpublic abstract class AbstractChatService implements IChatService{ @Override public void completions(ChatProcessAggregate chatProcess) { try { // 1.规则过滤(这里用到了使用次数校验和敏感词校验) RuleLogicEntity<ChatProcessAggregate> ruleLogicEntity = this.doCheckLogic(chatProcess ,DefaultLogicFactory.LogicModel.ACCESS_LIMIT.getCode()); // 判断校验是否通过 if(!LogicCheckTypeVO.SUCCESS.equals(ruleLogicEntity.getType())){ emitter.send(ruleLogicEntity.getInfo()); emitter.complete(); return emitter; } // 2.后续相关操作 } catch (Exception e) { // 异常处理 } } // 规则校验 protected abstract RuleLogicEntity<ChatProcessAggregate> doCheckLogic(ChatProcessAggregate chatProcessAggregate, String ... logics) throws Exception;}
校验规则抽象方法由子类实现
解释
@Servicepublic class ChatService extends AbstractChatService{ // 校验规则工厂 @Resource private DefaultLogicFactory logicFactory; @Override protected RuleLogicEntity<ChatProcessAggregate> doCheckLogic(ChatProcessAggregate chatProcess, String... logics) throws Exception { // 获取到校验规则map Map<String, ILogicFilter> logicFilterMap = logicFactory.openLogicFilter(); // 调用对应校验规则进行校验 RuleLogicEntity<ChatProcessAggregate> entity = null; for (String code : logics) { entity = logicFilterMap.get(code).fileter(chatProcess); if (!LogicCheckTypeVO.SUCCESS.equals(entity.getType())){ return entity; } } // entity 为 null的情况一般为传入的logics校验规则参数为空,即没有使用任何校验规则,这样也算校验通过 return entity != null ? entity : RuleLogicEntity.<ChatProcessAggregate>builder() .data(chatProcess).type(LogicCheckTypeVO.SUCCESS).build(); }}
此过程涉及到的一些其他类
校验结果类型值对象LogicCheckTypeVO
解释
@Getter@AllArgsConstructorpublic enum LogicCheckTypeVO { SUCCESS("0000", "校验通过"), REFUSE("0001","校验拒绝"), ; private final String code; private final String info;}
校验结果类,方便传递校验信息
解释
/** * @author www.luckysj.top 刘仕杰 * @description 规则校验结果类 * @create 2023/12/07 19:49:34 */@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class RuleLogicEntity <T> { private LogicCheckTypeVO type; private String info; private T data;}