V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NoKey
V2EX  ›  程序员

Java 代码 switch 分支过多,怎么改写比较优雅呢?

  •  
  •   NoKey · 2023-01-30 09:34:31 +08:00 · 5891 次点击
    这是一个创建于 667 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一个收到消息,分发给不同的处理方法的代码

    消息类型已经有十几种了

    使用 switch 来根据消息类型跳转不同的处理方法

    这个 switch 看起来就很庞大了

    请教一下,有没有很好的方式来重构这种情况的代码呢

    谢谢~

    51 条回复    2023-01-31 16:13:08 +08:00
    7911364440
        1
    7911364440  
       2023-01-30 09:35:11 +08:00
    策略模式啊
    dddd1919
        2
    dddd1919  
       2023-01-30 09:36:30 +08:00
    典型的工厂模式
    yaodao
        3
    yaodao  
       2023-01-30 09:38:45 +08:00 via iPhone
    如果 switch 中的内容比较简单,可以使用枚举类实现抽象方法的简单策略模式。如果比较复杂可以使用监听者模式,广播消息,但每个监听者只处理自己相应的消息。
    TWorldIsNButThis
        4
    TWorldIsNButThis  
       2023-01-30 09:39:22 +08:00 via iPhone
    使用语言内建的 dispatch 功能
    也就是子类型多态
    ql562482472
        5
    ql562482472  
       2023-01-30 09:42:17 +08:00
    switch 都写不优雅 你到底写成啥样了 发出来看看吧
    ashong
        6
    ashong  
       2023-01-30 09:55:21 +08:00 via iPhone   ❤️ 2
    handlers[event].action()
    xiangagou
        7
    xiangagou  
       2023-01-30 09:55:42 +08:00   ❤️ 1
    最通用的就是工厂+策略模式修改,几乎不用动脑子,其他模式得看业务匹配情况
    ashong
        8
    ashong  
       2023-01-30 09:56:31 +08:00 via iPhone
    @ashong 看错了😅
    nothingistrue
        9
    nothingistrue  
       2023-01-30 09:59:48 +08:00   ❤️ 8
    十几种还不算多,如果能做成枚举的话,switch 枚举并无不妥。如果太多做不成枚举,或者稍微有那么一两个变异的分支,用 if else 也不丢人。你要知道,你后面的处理是分发不同方法,那么就算有 50 个,代码行也就 150 行,不多。

    这里的重点,还是你的消息类型的命名规则。消息类型如果是依次罗列命名,超过 10 个就麻烦,超过 20 个就没法用了。基本上,超过 20 个类型,就该考虑对消息类型的名称进行归纳分级。如果消息类型有良好的命名规则,消息分发可能就不需要 swtich 、if else 了,十几行代码就能搞定。
    cosmain
        10
    cosmain  
       2023-01-30 10:02:24 +08:00
    @nothingistrue 一看就是老 coder 了
    fredli
        11
    fredli  
       2023-01-30 10:03:13 +08:00
    抽象类继承,消息子类,多态
    zhady009
        12
    zhady009  
       2023-01-30 10:04:25 +08:00
    每个消息类型对应一个 ActionChain, Action 对应具体的业务
    关于消息类型和 ActionChain 的关系建立可以是硬编码, 也可以用 Spring 的 BeanPostProcessor+注解的方式
    lysS
        13
    lysS  
       2023-01-30 10:14:07 +08:00
    信息类型是确定的,可以用数组
    lyusantu
        14
    lyusantu  
       2023-01-30 10:32:12 +08:00
    保持 switch 写法
    写完后折叠 switch 看起来就比较优雅了
    ianEros
        15
    ianEros  
       2023-01-30 10:34:07 +08:00
    复杂一点写策略,简单逻辑表驱动
    NoKey
        16
    NoKey  
    OP
       2023-01-30 10:37:04 +08:00
    @ql562482472 就是 switch 下面十几个 case ,可能还会增长,到一定时候,肯定不好看,一大片的 case😂
    lambdAlan
        17
    lambdAlan  
       2023-01-30 10:38:55 +08:00
    策略模式加工厂模式即可
    ianEros
        18
    ianEros  
       2023-01-30 10:40:23 +08:00
    Map<?, Function<?> action> actionsMap = new HashMap<>();

    // 初试配置对应动作
    actionsMap.put(value1, (someParams) -> { doAction1(someParams)});
    actionsMap.put(value2, (someParams) -> { doAction2(someParams)});
    actionsMap.put(value3, (someParams) -> { doAction3(someParams)});

    // 省略 null 判断
    actionsMap.get(param).apply(someParams);
    NoKey
        19
    NoKey  
    OP
       2023-01-30 10:46:48 +08:00
    @lyusantu 看不见就好了么?🤣
    libook
        20
    libook  
       2023-01-30 10:48:16 +08:00
    除非消息类型划分不合理,或者重复处理逻辑太多,否则不认为 switch 分枝多有什么问题。
    PiersSoCool
        21
    PiersSoCool  
       2023-01-30 10:52:24 +08:00
    别改了
    cubecube
        22
    cubecube  
       2023-01-30 10:53:09 +08:00
    大规模用策略模式的时候,代码给别人看的时候容易一坨屎,尤其是还是那种 switch 不稳定,状态可能相互有关联的时候。。。
    我个人倾向于不改 switch ,处理逻辑复杂的话,内容包在一个函数里面得了
    playtomandjerry
        23
    playtomandjerry  
       2023-01-30 10:53:34 +08:00   ❤️ 4
    不要整花里胡哨的优化,妈的,自己过段时间都看起来费劲,后面人接手能头疼死。switch 内不要放逻辑,直接抽方法出去,已经是很清晰的写法了,简单明了
    shaozelin030405
        24
    shaozelin030405  
       2023-01-30 10:58:08 +08:00
    switch 改成查表法(改成个 map ,根据 key 来看对应哪个情况),然后各个情况可以分成各个方法,好搞
    Ciallo
        25
    Ciallo  
       2023-01-30 10:58:13 +08:00   ❤️ 1
    9000 行的方法与 1000 个 if 🐶
    jorneyr
        26
    jorneyr  
       2023-01-30 10:58:23 +08:00
    可以参考 MyBatis 的 TypeHandlerRegistry 把各种消息的处理器集中注册。
    zoharSoul
        27
    zoharSoul  
       2023-01-30 11:05:25 +08:00
    @ianEros 特别烦这种, 还不如 if else
    cwcc
        28
    cwcc  
       2023-01-30 11:07:51 +08:00
    我倒是觉得 switch 没啥不妥,但 switch 从代码缩进层面会缩进两次,使用 if else 也才一次。

    如果过多的话,建议建个表,然后循环找表对应的方法。或者直接写个表驱动。

    不想想太多的话,你只需要做到,让每个 case 的意义十分明确,case 里面的代码不要写逻辑,只调用一两条外面的方法就行了。
    dcncy
        29
    dcncy  
       2023-01-30 11:14:35 +08:00 via iPhone
    各自的方法名与其消息类型保持一致,或者加个前后缀。反射调用方法,两三行代码搞定。
    bxb100
        30
    bxb100  
       2023-01-30 11:17:10 +08:00
    * switch 放到工厂类里面, case 的逻辑都抽成类, 注意 SRP
    * 使用责任链, 好处是不需要大量的 switch/if, 坏处是不好理解
    finab
        31
    finab  
       2023-01-30 11:21:14 +08:00
    6 楼的表驱动模式
    hyqCrystal
        32
    hyqCrystal  
       2023-01-30 11:21:27 +08:00
    工厂加策略
    qua
        33
    qua  
       2023-01-30 11:23:37 +08:00 via Android   ❤️ 2
    如果用工厂,创建策略的时候还不是要判断类型一长串 switch ,有区别吗
    coala
        34
    coala  
       2023-01-30 11:25:42 +08:00
    才十几个, 就这样吧, 几十个了可以用策略模式
    potatowish
        35
    potatowish  
       2023-01-30 11:37:14 +08:00 via iPhone   ❤️ 2
    可以使用策略模式,把不同的消息类型处理逻辑划分到独立的实现类中,这些实现类都实现一个公共接口,每个实现类添加一个枚举类型属性,区分消息类型。最后定义一个策略处理类,获取公共接口下所有实现类的对象,按照每次接收的消息类型过滤得到对应的实现类对象,调用其消息处理方法即可。
    Achieve7
        36
    Achieve7  
       2023-01-30 12:20:36 +08:00
    这是典型的策略+工厂的场景, 用个 dispatcher 进行分发就好了. 如果能够确定逻辑, 甚至直接拿枚举写也行
    fkdog
        37
    fkdog  
       2023-01-30 12:27:51 +08:00
    分情况。
    如果 switch 分支内的逻辑很简单,那么 switch 就是最优解,你做成多态并不会优雅到哪里去,分支判断的逻辑塞到各个子类里去的话反而不直观也不好维护。
    如果 switch 分支内的逻辑很复杂,涉及到 CRUD 、缓存、RPC 等调用,那么楼上有答案。
    levelworm
        38
    levelworm  
       2023-01-30 12:36:55 +08:00 via Android
    我感觉 switch 里每项不多的话,还是它最优雅。
    jinsongzhao
        39
    jinsongzhao  
       2023-01-30 12:51:28 +08:00
    把一个 switch 分门别类,变成多个 switch 或 if else ,可读性就挺好了。
    实在想减少代码,只能创建一个类型值和函数名称的对应表,然后把 switch 替换为通过类型查表得到函数名,然后反射调用。这样语句是少了,类型和函数名也都放在一起,类型和函数对应关系可读性优化了,但是逻辑复杂了,总体可读性其实是下降了。
    3dwelcome
        40
    3dwelcome  
       2023-01-30 12:52:56 +08:00
    我以前写过一篇 switch 的文章,"只有放弃英文代码,写全中文代码,才能支撑起千变万化的需求。", 可惜大家回复太热情,直接进水区了。

    单文件太大可以分几个子文件,但 Op 最主要的问题,就是消息的命名方式。

    起不好英文函数名字,后续维护就是灾难。
    WilliamYang
        41
    WilliamYang  
       2023-01-30 12:58:05 +08:00
    switch 分发已经是最优解了
    pennai
        42
    pennai  
       2023-01-30 12:58:42 +08:00
    策略模式先苟着,当策略模式也感觉过多的时候可以用规则引擎
    s1e42NxZVE484pwH
        43
    s1e42NxZVE484pwH  
       2023-01-30 15:07:28 +08:00
    ZiLong
        44
    ZiLong  
       2023-01-30 16:10:36 +08:00
    我看过一篇文章,switch 最大的问题还是扩展性问题(违背了开闭原则),然后里面列举了 7 种方式改善 switch ,基本的思想和方法都覆盖到了 [重构 Java switch 语句的七种方法]( https://juejin.cn/post/7149189951895617550)
    Cloutain
        45
    Cloutain  
       2023-01-30 17:32:07 +08:00
    那完了,反汇编引擎就是 switch 一层套一层
    ssdold
        46
    ssdold  
       2023-01-30 17:39:33 +08:00
    @Ciallo 一样一样的,if/else
    mmuggle
        47
    mmuggle  
       2023-01-30 17:41:50 +08:00
    既然要重构,那就用点设计模式吧,策略模式加工厂模式
    fuis
        48
    fuis  
       2023-01-30 18:25:41 +08:00
    别改了,能用就行
    zhouhu
        49
    zhouhu  
       2023-01-30 19:40:35 +08:00
    升级到 Java 17
    newmlp
        50
    newmlp  
       2023-01-31 09:37:40 +08:00
    十几种还多啊,一点都不多,没必要重构
    vinceall
        51
    vinceall  
       2023-01-31 16:13:08 +08:00
    我喜欢用枚举+函数接口,映射到不同处理策略
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1955 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 16:17 · PVG 00:17 · LAX 08:17 · JFK 11:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.