❓仓库地址:github.com/Chengyunlai…
😉作者:@Chengyunlai(这是我的语雀)
📩邮箱:yunlai_cheng@163.com
1. 介绍
应用背景:根据某些条件匹配相应子实现类的逻辑,见图 1-1
避免:使用IF-ELSE
来处理这样的逻辑。原因:不便于后续升级迭代的维护,可以仔细思考一下,如果思考不出来还有有疑问,可以提出问题:为什么不便于后续升级迭代。
图 1-1
2. 实战
背景: 大模型的背景下,我司有两个业务:
现在需要根据用户付费的状态,判断用户到底选择哪个模型
架构设计图:
整体架构设计如 图 1-2 所示,从Application
使用 ChatContext
这个类,传入 User
,让其(ChatContext
)调用getReplyInfo
,根据 User
的条件来执行ChatService
的实现类。
我知道,很多朋友肯定想在ChatContext
的getReplyInfo
这个方法中使用IF-ELSE
的方式来控制到底是具体调用3_5
还是4_0
,我们避免使用这样的方式,之前说了:这样不便于拓展代码。
解决的办法是使用枚举类,先上代码,然后去思考为什么使用枚举类就可以解决逻辑分支的判断了。
2.1. 为什么用枚举类
package top.chengyunlai.architecture.chat;
import java.util.Objects;
/**
* @author Chengyunlai
* @description: TODO
* @date 2023/7/25
*/
public enum ChatEnum {
CHAT_3_5(1,"chat3"),
CHAT_4_0(2,"chat4");
private Integer flag;
private String serverName;
ChatEnum(Integer flag, String serverName) {
this.flag = flag;
this.serverName = serverName;
}
public Integer getFlag() {
return flag;
}
public void setFlag(Integer flag) {
this.flag = flag;
}
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public static String getStrategyServiceByType(Integer type) {
for (ChatEnum d : ChatEnum.values()) {
if (Objects.equals(type, d.getFlag())) {
return d.getServerName();
}
}
// 兜底处理
return "chat3";
}
}
解读: 11 行和 12 行定义了两个枚举类型,flag
是条件:匹配用户传入的条件;serverName
是对应的服务名(Spring 容器名称),这样的定义就方便后面只需要通过值就能拿到对应的服务名称了。
然后我们封装了一个方法getStrategyServiceByType
,在 38 行。ChatEnum.values()
拿出我们定义的所有的枚举(flag,serverName)
,然后通过增强 for 循环去判断传入进来的类型去匹配对应的值。
当然兜底处理是返回"chat3"
优点: 这样做的好处就是,新增ChatService
子类的时候,只需要新增一个枚举即可。
2.2. ChatContext 的妙用
我们知道在 ChatContext 需要去调用不同的 ChatService 实现类,有没有什么好的方式将他们注入进来呢?
@Autowired
private Map chatServiceMap;
通过 IDEA 工具我们可以发现,它指向了两个实例(实例使用@Service
注解修饰),这说明使用自动注入的方式 + Map 的方式实际上已经做到了(key
:容器名,value
:容器实例)。
接下去要考虑的就是,如何搭配枚举类去取对应的 key 即可。
也就是getReplyInfo
方法
public String getReplyInfo(User user){
// 通过 Map 拿到名字对应的容器实例
return chatServiceMap.get(
// 通过枚举获得对应的容器名字
ChatEnum.getStrategyServiceByType(
// 条件
user.getIsChatGPTType()
)
).getReplyInfo();
}
3. 代码
懂了这个重要的枚举类,我们就来开发剩下的代码。
3.1. ChatService
package top.chengyunlai.architecture.chat;
public interface ChatService {
String getReplyInfo();
}
3.2. ChatServiceImpl3_5
package top.chengyunlai.architecture.chat.impl;
import org.springframework.stereotype.Service;
import top.chengyunlai.architecture.chat.ChatService;
/**
* @author Chengyunlai
* @description: TODO
* @date 2023/7/25
*/
@Service("chat3")
public class ChatServiceImpl3_5 implements ChatService {
@Override
public String getReplyInfo() {
return "chat3.5";
}
}
3.3. ChatServiceImpl4_0
package top.chengyunlai.architecture.chat.impl;
import org.springframework.stereotype.Service;
import top.chengyunlai.architecture.chat.ChatService;
/**
* @author Chengyunlai
* @description: TODO
* @date 2023/7/25
*/
@Service("chat4")
public class ChatServiceImpl4_0 implements ChatService {
@Override
public String getReplyInfo() {
return "chat4.0";
}
}
3.4. User 类
package top.chengyunlai.architecture.chat;
import lombok.Data;
/**
* @author Chengyunlai
* @description: TODO
* @date 2023/7/25
*/
public class User {
// 是否开启 ChatGPT4,1 为不开启;2 为开启
private Integer chatGPTType;
public void setChatGPTType(Integer chatGPTType) {
this.chatGPTType = chatGPTType;
}
public Integer getIsChatGPTType(){
return this.chatGPTType;
}
}
3.5. 枚举类
见上
3.6. ChatContext
package top.chengyunlai.architecture.chat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author Chengyunlai
* @description: 对话上下文,属于对话中间件,根据条件选择对应的真实对话服务
* @date 2023/7/25
*/
@Service
public class ChatContext {
@Autowired
private Map chatServiceMap;
public String getReplyInfo(User user){
// 通过 Map 拿到名字对应的容器实例
return chatServiceMap.get(
// 通过枚举获得对应的容器名字
ChatEnum.getStrategyServiceByType(
// 条件
user.getIsChatGPTType()
)
).getReplyInfo();
}
}
如果本文对你有帮助的话,记得点个赞再走呀。愿你在技术学习的路上不断的突飞猛进!