策略模式是干什么的 、怎么用 ?
定义
策略模式(Strategy Pattern)是一种行为设计模式,它定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户,从而达到算法的变化不会影响到客户。这种模式涉及到三个角色:
- 上下文(Context):持有一个策略类的引用,最终给客户端调用。
- 策略(Strategy):定义所有支持的算法的公共接口。Context使用这个接口来调用某个Concrete Strategy定义的算法。
- 具体策略(Concrete Strategy):实现Strategy接口的具体算法。
使用场景
- 当你想让一个对象有多种行为,而且想避免使用大量的条件语句时,可以使用策略模式。它提供了一种用条件语句以外的另一种选择,当你需要根据不同条件执行不同行为时特别有用。比如,一个排序的类,它支持多种排序算法,如冒泡排序、选择排序等,可以通过策略模式来实现这些算法的互换。
- 还有就是模拟多种营销类型的时候可以使用策略模式
营销类型实现的策略模式的简图
实现步骤
以下是使用Java实现策略模式的基本步骤:
- 定义策略接口:这个接口声明了算法的方法。
- 创建具体策略类:实现策略接口,封装了相关的算法和行为。
- 创建上下文类:持有策略类的引用,通过构造方法或者setter方法来接收策略对象。上下文可能定义了一些策略之间共享的数据。
- 客户端调用:客户端创建上下文对象,并传入具体的策略类,通过上下文对象来调用策略方法。
项目中实现的规则过滤
需求:
我们完成的ChatGPT项目, 虽然这里实现了问答的功能, 但是对于一个需要上线的项目来说,还需要必要的管理。 只有这样,你才不会被“约谈“。
因为ChatGPT是外国的东西, 他并不会对我们国内的敏感内容进行过滤, 虽然它本身也实现了相关的过滤功能, 但那是相对而言的, 所以如果你的网站被某些不良用户恶意利用。 到时候网站被强制下线, 损失的可就不是零星半点了。
所以, 我们在这里需要对用户输入的问答内容做敏感词过滤规则。 同时,如果每个用户都能够无限次数的使用你的项目, 那么你这个白菜被割的可就真香了。 所以我们还需要做限制使用次数的规则。再者, 如果如果多个用户在同一时间段内, 一直做问答,那么这个接口的访问粒度就会非常大, 所以我们还得需要做限频限次的操作。
为什么使用策略模式
频次、频率、白名单、敏感词等,都是用于支撑核心业务之外辅助流程,这些流程都是比较容易随着业务的变动而发生变化。所以我们要把这类东西分别封装起来,让它们的改变不会影响到核心业务流程。同时使用工厂服务, 实现业务的统一调用。经过这样一套策略模式 + 工厂服务的,我们实现的业务代码就会显得美观而又实用。
规则过滤的业务逻辑
实现过滤规则
首先根据工厂服务的特点, 我们首先定义**统一的规则接口(ILogicFilter接口)**, 这样其他模块调用的时候就有了统一的访问地址。
1 2 3 4 5 6 7 8 9
|
public interface ILogicFilter { RuleLogicEntity<ChatProcessAggregate> filter(ChatProcessAggregate chatProcess) throws Exception;
}
|
接下来,我们实现的两种规则算法就可以继承这个接口
访问次数限制算法
在这里我们通过访问配置文件中的白名单用户(无任何限制的用户), 来进行匹配。 如果未匹配,那么就需要将用户存入限制访问次数的缓存中。 完成问答之后visitCount进行累加。, 最后再通过配置的上下文类进行返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
@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; @Resource private Cache<String, Integer> visitCache;
@Override public RuleLogicEntity<ChatProcessAggregate> filter(ChatProcessAggregate chatProcess) throws Exception {
log.info("访问的用户(Token):{}, openId是:{}", chatProcess.getToken(), chatProcess.getOpenid()); if(chatProcess.isWhiteList(whiteListStr)){ return RuleLogicEntity.<ChatProcessAggregate>builder() .type(RuleLogicEntity.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(RuleLogicEntity.LogicCheckTypeVO.SUCCESS) .data(chatProcess) .build(); } return RuleLogicEntity.<ChatProcessAggregate>builder() .type(RuleLogicEntity.LogicCheckTypeVO.REFUSE) .data(chatProcess) .info("您今日的免费 " + limitCount + "次,已耗尽。 请明天再来!!!").build(); }
}
|
敏感词过滤规则算法
同样的, 首先判断是否为白名单用户, 如果不是再进行规则过滤
通过解析携带用户问答消息的ChatProcessAggregate, 得到用户的问答内容。
接下来通过SensitiveWordBs敏感词库,实现对用户问答Content的检测。如果检测到了敏感词, 那么就直接返回即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
@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 { if (chatProcess.isWhiteList(whiteListStr)){ return RuleLogicEntity.<ChatProcessAggregate>builder() .type(RuleLogicEntity.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 checkedMessage = words.replace(content); return MessageEntity.builder() .content(checkedMessage) .role(message.getRole()) .name(message.getName()) .build(); }).collect(Collectors.toList());
newChatProcessAggregate.setMessages(newMessages); return RuleLogicEntity.<ChatProcessAggregate>builder() .type(RuleLogicEntity.LogicCheckTypeVO.SUCCESS) .data(newChatProcessAggregate) .build(); } }
|
工厂服务暴露规则
我们这里使用的工厂服务是简单工厂模式中提供的工厂服务,他的作用是:
- 集中管理:将规则类的管理集中到一个位置
- 解耦:客户端与具体产品的创建过程解耦,客户端不需要知道如何创建对象,只需要知道工厂类。
当然我们这里的统一管理并不是通过if-else这种方式实现, 而不同过map存储。 同时在不同的规则算法上通过自定义注解 LogicStrategy 将各类规则通过工厂的方式进行处理并对外提供服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
@Service public class DefaultLogicFactory {
public 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) { logicFilterMap.put(strategy.logicMode().getCode(), logic); } }); } public Map<String, ILogicFilter> openLogicFilter() { return logicFilterMap; }
}
|
在业务中使用规则
应答服务业务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @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));
RuleLogicEntity<ChatProcessAggregate> ruleLogicEntity = this.doCheckLogic(chatProcess, LogicModel.ACCESS_LIMIT.getCode(), LogicModel.SENSITIVE_WORD.getCode()); if (!LogicCheckTypeVO.SUCCESS.equals(ruleLogicEntity.getType())) { emitter.send(ruleLogicEntity.getInfo()); emitter.complete(); return emitter; } this.doMessageResponse(ruleLogicEntity.getData(), emitter); } catch (Exception e) { throw new ChatGPTException(Constants.ResponseCode.UN_ERROR.getCode(), Constants.ResponseCode.UN_ERROR.getInfo()); } return emitter; } protected abstract RuleLogicEntity<ChatProcessAggregate> doCheckLogic(ChatProcessAggregate chatProcess, String... logics) throws Exception;
protected abstract void doMessageResponse(ChatProcessAggregate chatProcess, ResponseBodyEmitter responseBodyEmitter) throws JsonProcessingException;
}
|
规则过滤调用:
1 2 3 4 5 6 7 8 9 10
| RuleLogicEntity<ChatProcessAggregate> ruleLogicEntity = this.doCheckLogic(chatProcess, LogicModel.ACCESS_LIMIT.getCode(), LogicModel.SENSITIVE_WORD.getCode()); if (!LogicCheckTypeVO.SUCCESS.equals(ruleLogicEntity.getType())) { emitter.send(ruleLogicEntity.getInfo()); emitter.complete(); return emitter; }
|
doCheckLogic规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Service public class ChatService extends AbstractChatService { @Resource private DefaultLogicFactory logicFactory; @Override protected RuleLogicEntity<ChatProcessAggregate> doCheckLogic(ChatProcessAggregate chatProcess, String... logics) throws Exception { Map<String, ILogicFilter> logicFilterMap = logicFactory.openLogicFilter(); RuleLogicEntity<ChatProcessAggregate> entity = null; for (String code : logics) { entity = logicFilterMap.get(code).filter(chatProcess); if (!LogicCheckTypeVO.SUCCESS.equals(entity.getType())) return entity; } return entity != null ? entity : RuleLogicEntity.<ChatProcessAggregate>builder() .type(LogicCheckTypeVO.SUCCESS).data(chatProcess).build(); }
|
过滤规则实现所需model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class ChatProcessAggregate { private String openid; private String token; private String model = ChatGPTModel.GPT_3_5_TURBO.getCode(); private List<MessageEntity> messages;
public boolean isWhiteList(String whiteListStr) { String[] whiteList = whiteListStr.split(Constants.SPLIT); for (String whiteOpenid : whiteList) { if (whiteOpenid.equals(openid)) return true; } return false; }
}
|
1 2 3 4 5 6 7 8 9 10 11
|
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface LogicStrategy {
LogicModel logicMode();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@Data @NoArgsConstructor @AllArgsConstructor @Builder public class RuleLogicEntity<T> {
private LogicCheckTypeVO type; private String info; private T data;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Getter @AllArgsConstructor public enum LogicCheckTypeVO {
SUCCESS("0000", "校验通过"), REFUSE("0001","校验拒绝"), ;
private final String code; private final String info;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
@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; } }
|
说明
学习自: 小傅哥项目 https://bugstack.cn/md/develop/design-pattern/2020-07-05-%E9%87%8D%E5%AD%A6%20Java%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8A%E5%AE%9E%E6%88%98%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E3%80%8B.html