定义
策略模式(Strategy Pattern)是一种行为设计模式,它定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户,从而达到算法的变化不会影响到客户。这种模式涉及到三个角色:
使用场景
营销类型实现的策略模式的简图
实现步骤
以下是使用Java实现策略模式的基本步骤:
需求:
我们完成的ChatGPT项目, 虽然这里实现了问答的功能, 但是对于一个需要上线的项目来说,还需要必要的管理。 只有这样,你才不会被“约谈“。
因为ChatGPT是外国的东西, 他并不会对我们国内的敏感内容进行过滤, 虽然它本身也实现了相关的过滤功能, 但那是相对而言的, 所以如果你的网站被某些不良用户恶意利用。 到时候网站被强制下线, 损失的可就不是零星半点了。
所以, 我们在这里需要对用户输入的问答内容做敏感词过滤规则。 同时,如果每个用户都能够无限次数的使用你的项目, 那么你这个白菜被割的可就真香了。 所以我们还需要做限制使用次数的规则。再者, 如果如果多个用户在同一时间段内, 一直做问答,那么这个接口的访问粒度就会非常大, 所以我们还得需要做限频限次的操作。
为什么使用策略模式
频次、频率、白名单、敏感词等,都是用于支撑核心业务之外辅助流程,这些流程都是比较容易随着业务的变动而发生变化。所以我们要把这类东西分别封装起来,让它们的改变不会影响到核心业务流程。同时使用工厂服务, 实现业务的统一调用。经过这样一套策略模式 + 工厂服务的,我们实现的业务代码就会显得美观而又实用。
规则过滤的业务逻辑
首先根据工厂服务的特点, 我们首先定义**统一的规则接口(ILogicFilter接口)**, 这样其他模块调用的时候就有了统一的访问地址。
/**
* 规则过滤统一接口
*/
public interface ILogicFilter {
// 规则校验方法, 实现敏感词过滤和访问次数
RuleLogicEntity<ChatProcessAggregate> filter(ChatProcessAggregate chatProcess) throws Exception;
}
接下来,我们实现的两种规则算法就可以继承这个接口
访问次数限制算法
在这里我们通过访问配置文件中的白名单用户(无任何限制的用户), 来进行匹配。 如果未匹配,那么就需要将用户存入限制访问次数的缓存中。 完成问答之后visitCount进行累加。, 最后再通过配置的上下文类进行返回。
/**
* 访问次数过滤
* 通过继承规则过滤接口, 实现规则过滤的次数限制规则,这里我们实现对每个用户限制使用的次数是10次
*/
@Slf4j
@Component
@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.ACCESS_LIMIT)
public class AccessLimitFilter implements ILogicFilter {
@Value("${app.config.limit-count:10}")
private Integer limitCount; // 限制访问次数
@Value("${app.config.white-list}")
private String whiteListStr; // 不进行限制次数的白名单用户, 后续需要优化作为数组来实现, 并且不能在配置类中实现
// todo 后续使用数据库之后, 可以通过使用定时任务的方式来做缓存
@Resource
private Cache<String, Integer> visitCache; // 访问次数的缓存
@Override
public RuleLogicEntity<ChatProcessAggregate> filter(ChatProcessAggregate chatProcess) throws Exception {
log.info("访问的用户(Token):{}, openId是:{}", chatProcess.getToken(), chatProcess.getOpenid());
// 1. 对白名单中的用户直接放行
if(chatProcess.isWhiteList(whiteListStr)){
return RuleLogicEntity.<ChatProcessAggregate>builder()
.type(RuleLogicEntity.LogicCheckTypeVO.SUCCESS)
.data(chatProcess)
.build();
}
//2. 如果不是, 那么就先查找用户使用的次数 , 然后计算剩余次数, 最后给用户响应
String openId = chatProcess.getOpenid();
int visitCount = visitCache.get(openId, () -> 0);
// 2.1 判断次数是否超过限制次数
if(visitCount < limitCount) {
visitCache.put(openId, visitCount + 1);
return RuleLogicEntity.<ChatProcessAggregate>builder()
.type(RuleLogicEntity.LogicCheckTypeVO.SUCCESS)
.data(chatProcess)
.build();
}
// 2.2 如果是,直接进行false
return RuleLogicEntity.<ChatProcessAggregate>builder()
.type(RuleLogicEntity.LogicCheckTypeVO.REFUSE)
.data(chatProcess)
.info("您今日的免费 " + limitCount + "次,已耗尽。 请明天再来!!!").build();
}
}
敏感词过滤规则算法
同样的, 首先判断是否为白名单用户, 如果不是再进行规则过滤
通过解析携带用户问答消息的ChatProcessAggregate, 得到用户的问答内容。
接下来通过SensitiveWordBs敏感词库,实现对用户问答Content的检测。如果检测到了敏感词, 那么就直接返回即可。
/**
* 实现规则过滤接口, 判断用户输入的内容是否为敏感词中的内容, 如果是, 那么就拒绝回答
*/
@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> filter(ChatProcessAggregate chatProcess) throws Exception
//2. 如果不是 白名单用户, 那么需要进行判断
//2.1 创建一个新的ChatProcessAggregate, 然后获取用户会话携带的所有信息
ChatProcessAggregate newChatProcessAggregate = new ChatProcessAggregate();
newChatProcessAggregate.setOpenid(chatProcess.getOpenid());
newChatProcessAggregate.setModel(chatProcess.getModel());
//2.2 获取用户输入的内容, 进行判断
List<MessageEntity> newMessages = chatProcess.getMessages().stream()
.map(message -> ).collect(Collectors.toList());
newChatProcessAggregate.setMessages(newMessages);
// 2.3 将敏感词进行过滤之后, 返回新的用户聊天信息的聚合ChatProcessAggregate
return RuleLogicEntity.<ChatProcessAggregate>builder()
.type(RuleLogicEntity.LogicCheckTypeVO.SUCCESS)
.data(newChatProcessAggregate)
.build();
}
}
工厂服务暴露规则
我们这里使用的工厂服务是简单工厂模式中提供的工厂服务,他的作用是:
当然我们这里的统一管理并不是通过if-else这种方式实现, 而不同过map存储。 同时在不同的规则算法上通过自定义注解 LogicStrategy 将各类规则通过工厂的方式进行处理并对外提供服务。
/**
* @description 规则工厂
* 实现规则过滤接口所需要实现的内容
*/
@Service
public class DefaultLogicFactory
});
}
// 将获取的注解内容暴露出去, 供服务访问
public Map<String, ILogicFilter> openLogicFilter() {
return logicFilterMap;
}
}
在业务中使用规则
应答服务业务
@Slf4j
public abstract class AbstractChatService implements IChatService {
@Resource
protected OpenAiSession openAiSession;
@Override
public ResponseBodyEmitter completions(ResponseBodyEmitter emitter, ChatProcessAggregate chatProcess) {
try {
emitter.onCompletion(() -> {
log.info("流式问答请求完成,使用模型:{}", chatProcess.getModel());
});
emitter.onError(throwable -> log.error("流式问答请求疫情,使用模型:{}", chatProcess.getModel(), throwable));
// 1. 校验权限
// 在authController中已经实现了
// 2. 规则过滤
RuleLogicEntity<ChatProcessAggregate> ruleLogicEntity = this.doCheckLogic(chatProcess,
LogicModel.ACCESS_LIMIT.getCode(),
LogicModel.SENSITIVE_WORD.getCode());
// 规则校验失败, 直接返回。 不进行问答
if (!LogicCheckTypeVO.SUCCESS.equals(ruleLogicEntity.getType()))
// 3. 应答处理
// todo 请求问答这里
this.doMessageResponse(ruleLogicEntity.getData(), emitter);
} catch (Exception e)
// 4. 返回结果
return emitter;
}
//检查提供的规则过滤逻辑
protected abstract RuleLogicEntity<ChatProcessAggregate> doCheckLogic(ChatProcessAggregate chatProcess, String... logics) throws Exception;
protected abstract void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter responseBodyEmitter) throws JsonProcessingException;
}
规则过滤调用:
// 2. 规则过滤
RuleLogicEntity<ChatProcessAggregate> ruleLogicEntity = this.doCheckLogic(chatProcess,
LogicModel.ACCESS_LIMIT.getCode(),
LogicModel.SENSITIVE_WORD.getCode());
// 规则校验失败, 直接返回。 不进行问答
if (!LogicCheckTypeVO.SUCCESS.equals(ruleLogicEntity.getType()))
doCheckLogic规则
/**
*/
@Service
public class ChatService extends AbstractChatService
return entity != null ? entity : RuleLogicEntity.<ChatProcessAggregate>builder()
.type(LogicCheckTypeVO.SUCCESS).data(chatProcess).build();
}
过滤规则实现所需model
/**
* 用户聊天信息的聚合
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChatProcessAggregate
return false;
}
}
/**
* 通过自定义注解 LogicStrategy 将各类规则通过工厂的方式进行处理并对外提供服务。
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogicStrategy {
LogicModel logicMode();
}
/**
* 规则校验结果实体
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class RuleLogicEntity<T> {
private LogicCheckTypeVO type; // 规则校验类型
private String info; // 规则校验信息
private T data;
}
/**
* @description 逻辑校验类型,值对象
*/
@Getter
@AllArgsConstructor
public enum LogicCheckTypeVO {
SUCCESS("0000", "校验通过"),
REFUSE("0001","校验拒绝"),
;
private final String code;
private final String info;
}
/**
* 规则过滤种类的逻辑枚举
*/
@Getter
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;
}
}
学习自: 小傅哥项目








