使用工厂,策略,模板设计模式,实现规则校验功能

311 阅读4分钟

【设计模式】使用工厂,策略,模板设计模式,实现规则校验功能

前言

频次、频率、白名单、敏感词等,都是用于支撑核心业务之外辅助流程,这些流程都是比较容易随着业务的变动而发生变化。所以我们要把这类东西设计在核心流程之外,而不能直接把规则的代码与核心业务的代码写在一块。因为区分不出边界的代码,会让工程的腐化程度不断加剧。

实现

本文示例为实现一个敏感词校验和使用次数校验

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;}