工厂模式 枚举类

2023年 7月 26日 49.6k 0

❓仓库地址:github.com/Chengyunlai…

😉作者:@Chengyunlai(这是我的语雀)

📩邮箱:yunlai_cheng@163.com

1. 介绍

应用背景:根据某些条件匹配相应子实现类的逻辑,见图 1-1

避免:使用IF-ELSE来处理这样的逻辑。原因:不便于后续升级迭代的维护,可以仔细思考一下,如果思考不出来还有有疑问,可以提出问题:为什么不便于后续升级迭代。

图 1-1

2. 实战

背景: 大模型的背景下,我司有两个业务:

  • ChatGPT3_5;
  • ChatGPT4_0
  • 现在需要根据用户付费的状态,判断用户到底选择哪个模型

    架构设计图:

    整体架构设计如 图 1-2 所示,从Application使用 ChatContext 这个类,传入 User,让其(ChatContext)调用getReplyInfo,根据 User的条件来执行ChatService的实现类。

    我知道,很多朋友肯定想在ChatContextgetReplyInfo这个方法中使用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 实现类,有没有什么好的方式将他们注入进来呢?

  • 通过枚举类,获取获得对应容器的名字:String
  • 那么通过这个名字就应该能拿到这些容器对应的实例,Yes,Spring 帮我们能做到这一点,关键点就在这个 Map 形式的注入
  • @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();
        }
    }
    

    如果本文对你有帮助的话,记得点个赞再走呀。愿你在技术学习的路上不断的突飞猛进!

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论