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

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

  •  
  •   NoKey · 237 天前 · 5272 次点击
    这是一个创建于 237 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    消息类型已经有十几种了

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

    这个 switch 看起来就很庞大了

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

    谢谢~

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

    这里的重点,还是你的消息类型的命名规则。消息类型如果是依次罗列命名,超过 10 个就麻烦,超过 20 个就没法用了。基本上,超过 20 个类型,就该考虑对消息类型的名称进行归纳分级。如果消息类型有良好的命名规则,消息分发可能就不需要 swtich 、if else 了,十几行代码就能搞定。
    cosmain
        10
    cosmain  
       237 天前
    @nothingistrue 一看就是老 coder 了
    fredli
        11
    fredli  
       237 天前
    抽象类继承,消息子类,多态
    zhady009
        12
    zhady009  
       237 天前
    每个消息类型对应一个 ActionChain, Action 对应具体的业务
    关于消息类型和 ActionChain 的关系建立可以是硬编码, 也可以用 Spring 的 BeanPostProcessor+注解的方式
    lysS
        13
    lysS  
       237 天前
    信息类型是确定的,可以用数组
    lyusantu
        14
    lyusantu  
       237 天前
    保持 switch 写法
    写完后折叠 switch 看起来就比较优雅了
    ianEros
        15
    ianEros  
       237 天前
    复杂一点写策略,简单逻辑表驱动
    NoKey
        16
    NoKey  
    OP
       237 天前
    @ql562482472 就是 switch 下面十几个 case ,可能还会增长,到一定时候,肯定不好看,一大片的 case😂
    lambdAlan
        17
    lambdAlan  
       237 天前
    策略模式加工厂模式即可
    ianEros
        18
    ianEros  
       237 天前
    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
       237 天前
    @lyusantu 看不见就好了么?🤣
    libook
        20
    libook  
       237 天前
    除非消息类型划分不合理,或者重复处理逻辑太多,否则不认为 switch 分枝多有什么问题。
    PiersSoCool
        21
    PiersSoCool  
       237 天前
    别改了
    cubecube
        22
    cubecube  
       237 天前
    大规模用策略模式的时候,代码给别人看的时候容易一坨屎,尤其是还是那种 switch 不稳定,状态可能相互有关联的时候。。。
    我个人倾向于不改 switch ,处理逻辑复杂的话,内容包在一个函数里面得了
    playtomandjerry
        23
    playtomandjerry  
       237 天前   ❤️ 4
    不要整花里胡哨的优化,妈的,自己过段时间都看起来费劲,后面人接手能头疼死。switch 内不要放逻辑,直接抽方法出去,已经是很清晰的写法了,简单明了
    shaozelin030405
        24
    shaozelin030405  
       237 天前
    switch 改成查表法(改成个 map ,根据 key 来看对应哪个情况),然后各个情况可以分成各个方法,好搞
    Ciallo
        25
    Ciallo  
       237 天前   ❤️ 1
    9000 行的方法与 1000 个 if 🐶
    jorneyr
        26
    jorneyr  
       237 天前
    可以参考 MyBatis 的 TypeHandlerRegistry 把各种消息的处理器集中注册。
    zoharSoul
        27
    zoharSoul  
       237 天前
    @ianEros 特别烦这种, 还不如 if else
    cwcc
        28
    cwcc  
       237 天前
    我倒是觉得 switch 没啥不妥,但 switch 从代码缩进层面会缩进两次,使用 if else 也才一次。

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

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

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

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