V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
hyyy
V2EX  ›  程序员

如何完美的消除 if...else..或者 switch 这种代码,求大神给个思路。

  •  
  •   hyyy · 2016-06-30 10:55:29 +08:00 · 17141 次点击
    这是一个创建于 3097 天前的主题,其中的信息可能已经有所发展或是发生改变。

    举个例子,在 iOS 开发中,经常会遇到在设置 tableViewCell 时需要根据 indexPath 来设置不同 cell ,一般代码可能会是下面这个样子:

    - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
        
        // 设置第一种 cell
        if (indexPath.section == 3 && indexPath.row != 2) {
            //  cell 代码
            return cell;
        }
        
        // 设置第二种 cell
        if (indexPath.section == 3 && indexPath.row == 2) {
            // cell 代码
            return cell;
        }
        
        // 设置其他 cell
        return cell;
    }
    
    

    当然,举的例子比较简单, if...else...也不多,这种场景在开发中很多,一般使用 if...else...或者 switch 时会使代码非常多,求大神们能不能给个思路,遇到这种场景尽量不要出现这种嵌套代码,最好给个实例膜拜膜拜 ^_^

    86 条回复    2016-07-01 22:17:23 +08:00
    wander2008
        1
    wander2008  
       2016-06-30 10:59:19 +08:00 via iPhone
    不可能
    ourcubk
        2
    ourcubk  
       2016-06-30 10:59:51 +08:00
    不可能
    chairuosen
        3
    chairuosen  
       2016-06-30 11:02:33 +08:00
    switch 可以打表,简单 if 也可以
    zhuangzhuang1988
        4
    zhuangzhuang1988  
       2016-06-30 11:02:49 +08:00   ❤️ 4
    1. 表驱动法(看: 代码大全)
    2. 使用 match(看: F#, Scala)'
    3. 使用类+继承(看: 设计模式)
    Jabin
        5
    Jabin  
       2016-06-30 11:03:11 +08:00
    封装 工厂
    ipconfiger
        6
    ipconfiger  
       2016-06-30 11:06:31 +08:00   ❤️ 1
    该用则用, if else 是基础逻辑, 如果不是深层次嵌套的话, 最好不要作一些奇技淫巧出来
    hyyy
        7
    hyyy  
    OP
       2016-06-30 11:07:12 +08:00
    @wander2008
    其实我觉得这是一个有趣的话题^_^
    kera0a
        8
    kera0a  
       2016-06-30 11:10:24 +08:00
    SEL methods[][3] = {
    { @selector(getFirstCell:) },
    { @selector(getSecondCell:) },
    { @selector(getThirdCell:) },
    };

    SEL sel = methods[indexPath.section][indexPath.row];
    [self performSelector:sel withObject:indexPath];
    hasbug
        9
    hasbug  
       2016-06-30 11:14:16 +08:00
    这能避免吗? 除非不做判断···
    stcasshern
        10
    stcasshern  
       2016-06-30 11:16:05 +08:00
    这个不能吧,印象中这个是最基础得
    ma125125t
        11
    ma125125t  
       2016-06-30 11:19:24 +08:00
    目前的 tableView 就是这么艹蛋。但已经有很多开源的方法解决这个问题了(想象一下一百种 cell ,分为数十个 section 和 row 的情况)。去找找吧。
    michaelye1988
        12
    michaelye1988  
       2016-06-30 11:23:45 +08:00   ❤️ 2
    我认为这种情况下用 if else 或者 switch 是非常合理的,至少代码可读性还是在的。如果非要套用一些设计模式什么的,维护起来就痛苦了。
    chmlai
        13
    chmlai  
       2016-06-30 11:24:15 +08:00
    这个简单, 用数据驱动就清晰多了
    spongebobsun
        14
    spongebobsun  
       2016-06-30 11:24:39 +08:00
    @kera0a 学习了
    mygirl
        15
    mygirl  
       2016-06-30 11:25:06 +08:00
    @ma125125t 几十种的 Cell 这应该是属于不合理的设计吧?
    chunqiuyiyu
        16
    chunqiuyiyu  
       2016-06-30 11:27:11 +08:00
    完美消除是不可能的,不然这几个关键字就不会出现。重要的是理清逻辑,不要嵌套过深。
    rekulas
        17
    rekulas  
       2016-06-30 11:27:43 +08:00
    看不懂 两种都是返回 cell 条件为什么不整合在一起
    8bit
        18
    8bit  
       2016-06-30 11:31:19 +08:00 via Android
    查表,代码大全
    subpo
        19
    subpo  
       2016-06-30 11:34:25 +08:00
    能只写 if 不写 else 不错了
    harry890829
        20
    harry890829  
       2016-06-30 11:37:08 +08:00
    这种平级的 if...else...已经不错了,我刚接手了一个代码,到手的时候 4-5 层 if 结构嵌套,全都带 else ,因为需求的关系,我现在又在里面加了两层 if..else...结构,我已经恶心死了
    woshicixide
        21
    woshicixide  
       2016-06-30 11:39:50 +08:00 via Android
    多态
    jason19659
        22
    jason19659  
       2016-06-30 11:42:46 +08:00
    cell = indexPath.section == 3 ? a : indexPath.row == 2 ? b : c
    zhicheng
        23
    zhicheng  
       2016-06-30 11:46:00 +08:00
    静态结构这样写没问题,把 3 和 2 消除就行,用 USER_SECTION 常量定义一下。
    动态结构用 model 来判断。
    fatedier
        24
    fatedier  
       2016-06-30 12:04:08 +08:00
    简单工厂模式 + 反射,可以实现根据不同的字符串执行不同业务逻辑,但是不适合用这个替换掉所有的 if-else ,不然就是过度设计了。

    之前用 c++ 写过一个例子: http://blog.fatedier.com/2015/03/04/decoupling-by-using-reflect-and-simple-factory-pattern-in-cpp/
    lingoerer
        25
    lingoerer  
       2016-06-30 12:25:44 +08:00
    1 :[CellViewModel] -> 把每一个 Cell 的 ViewModel 定义出来,放进数组
    2 : cellViewModel.identifier, cell.render(cellViewModel) -> 每个 ViewModel 定义自己要用什么 Cell 来展示, dequeue 的时候出不同的 Cell ,然后 Cell 自己对应着绑 UI
    3 : tableView 的回调中直接一行同样的代码把 Cell 弄出来

    当然,前面还有个:
    0 :把你要显示的 Model 的内容 map 成 CellViewModel 的数组
    ibigbug
        26
    ibigbug  
       2016-06-30 12:31:32 +08:00
    muller
        27
    muller  
       2016-06-30 12:34:48 +08:00
    策略模式
    davisz
        28
    davisz  
       2016-06-30 12:36:02 +08:00
    do {
    if (false) break;
    }while(0);
    ilotuo
        29
    ilotuo  
       2016-06-30 12:37:31 +08:00
    这样?
    ```java
    cell = indexPath.section == 3 ? CellFactory.build(...) : indexPath.row == 2 ? CellFactory.build(...): null;
    ```
    xwartz
        30
    xwartz  
       2016-06-30 13:17:36 +08:00
    map
    mdluo
        31
    mdluo  
       2016-06-30 13:24:43 +08:00
    分支存数组,条件用位运算
    ihuotui
        32
    ihuotui  
       2016-06-30 13:31:24 +08:00 via Android
    责任连模式,差不多
    hantsy
        33
    hantsy  
       2016-06-30 13:33:01 +08:00
    State 模式
    twoyuan
        34
    twoyuan  
       2016-06-30 13:42:16 +08:00
    首先应该消灭的不是 Magic Number 吗……
    sutra
        35
    sutra  
       2016-06-30 13:46:55 +08:00
    Map
    youyongsong
        36
    youyongsong  
       2016-06-30 14:00:32 +08:00
    pattern match
    0x5e
        37
    0x5e  
       2016-06-30 14:01:41 +08:00
    swift 可能会稍微简化点
    let path = (indexPath.section, indexPath.row)
    switch(path) {
    case (0, 0):
    //xxxx
    case (0, 1):
    //xxxx
    }
    JasperYanky
        38
    JasperYanky  
       2016-06-30 14:02:42 +08:00
    model 决定 view controller 层不关心数据 只负责传递
    fhefh
        39
    fhefh  
       2016-06-30 14:06:15 +08:00
    loveuqian
        40
    loveuqian  
       2016-06-30 14:09:07 +08:00
    之前也是这样判断行数,后来改判断模型 title 了
    因为判断行数,一不小心,在第 0 行前面加一行,你这所有判断都要重写啊。。
    vincentxue
        41
    vincentxue  
       2016-06-30 14:13:31 +08:00
    看来 iOS 迫切需要一个为 UITableView 或者 UICollectionView 解决数据源封装的库。
    sensui7
        42
    sensui7  
       2016-06-30 14:22:34 +08:00
    OO 中的 tell, don't ask 原则可以消除这种大量的 else , if else 多说明代码抽象的不好, 根据你的逻辑,可以采用相应的设计模式解决
    lalalafq
        43
    lalalafq  
       2016-06-30 14:29:03 +08:00
    所有的数据源逻辑(高度,点击,渲染)全部丢到一个 array ;基础的 tableView 委托永远只有大概 10 行左右;
    绝壁高聚合,低耦合;产品想怎么改就怎么改;顺序啥的都是小问题。
    JohnSmith
        44
    JohnSmith  
       2016-06-30 14:33:48 +08:00
    rust 中有个 match
    skyoojaa
        45
    skyoojaa  
       2016-06-30 15:05:24 +08:00 via Android
    If...else...没法消除,但可以封装判断,让 if...else...里面的代码看起来简洁一些
    realpg
        46
    realpg  
       2016-06-30 15:22:54 +08:00
    我觉得这种代码很好
    瞎搞弄得可维护性差才是大坑
    iyeatse
        47
    iyeatse  
       2016-06-30 15:40:35 +08:00
    dorentus
        48
    dorentus  
       2016-06-30 15:41:54 +08:00
    @realpg 在最前面加一行的话,要从上到下把数字都改掉,也叫可维护性好么……
    so898
        49
    so898  
       2016-06-30 15:56:52 +08:00
    真要很多的话不应该是自定义 Cell ,然后把代码放到 Cell 里面去么?
    如果说不能自定义 Cell ,那不是应该通过在外面建立 Cell 内容填充生成的方法来做到精简这一块的代码么?
    missdeer
        50
    missdeer  
       2016-06-30 16:38:21 +08:00
    你看,用 OO 设计模式的时候到了,上面说不行的都不是那个年代过来的人
    weirdyu
        51
    weirdyu  
       2016-06-30 16:53:45 +08:00
    你这种按照行数来判断返回何种 cell 的方式不太好
    zjyjer
        52
    zjyjer  
       2016-06-30 17:05:44 +08:00
    1. 模式匹配
    2. 设计一个状态机 每个分支里的代码抽象出一个函数异步调用之
    programgou
        53
    programgou  
       2016-06-30 17:11:55 +08:00
    我认为我们可以专门提供一个 indexPath 的接口,然后再提供一个 builder ,把实现了 indexPath 接口的类的实例传递给 builder , builder 就可以返回一个 cell 。这样无论你有多少种 indexPath ,只需要维护 builder ,而不会改变 tableView 的任何代码。当你觉得你现在的 builder 过于复杂的时候,你甚至可以为这个 builder 提供接口,这样你可以在不改变原来代码的基础上,替换掉原来的 builder 。总之,类与类建立关系要通过接口,这样就可以在不改变代码的基础上,换掉一个类。也就是换掉一个功能。

    这里面涉及到一些设计模式的东西,不过楼主别怕,看不懂书建议去看源码,我博客上写过一个关于建构者模式的分析,你可以参考一下,不过要解决你的问题一个建构者模式肯定是不够的,还有工厂模式。
    sablib
        54
    sablib  
       2016-06-30 17:13:05 +08:00
    如果是 swift 的话,就用 enum 了。
    billion
        55
    billion  
       2016-06-30 17:29:57 +08:00
    如果语言是 Python 的话,可以使用字典解决:
    ```
    def a(para):
    xxx

    def b(para):
    xxx

    def c(para):
    xxx

    functionDict = {'a':a, 'b': b, 'c': c}

    result = functionDict[name](para)
    ```
    这样,当你需要使用 switch 或者 if ...else 来调用函数的时候,可以通过: functionDict('a')(para)这种方式调用函数。
    chrisstyle
        56
    chrisstyle  
       2016-06-30 17:36:22 +08:00
    同 25 楼~
    推荐
    i4mszengg
        57
    i4mszengg  
       2016-06-30 18:02:04 +08:00
    我想应该不用故意写一些奇葩的逻辑,日后维护很痛苦的吧,如果 单个 if 的 condiftions 比较多,可以嵌套 if , 这样更容易懂些,日后好维护。
    jasonlz
        58
    jasonlz  
       2016-06-30 18:25:42 +08:00
    用状态机。
    ooonme
        59
    ooonme  
       2016-06-30 18:30:40 +08:00 via iPhone
    @zjyjer 正解
    djyde
        60
    djyde  
       2016-06-30 18:34:00 +08:00
    程序之所以是程序,因为它能 if else
    EAimTY
        61
    EAimTY  
       2016-06-30 18:37:28 +08:00 via Android
    不可能的,不论用什么方法代替,底层肯定还会是 if else
    adrianzhang
        62
    adrianzhang  
       2016-06-30 18:50:00 +08:00 via Android
    结构化语言之前,都用 goto ,汇编里见 jump 更多。
    xwing
        63
    xwing  
       2016-06-30 20:06:43 +08:00
    命令链模式。
    hrong
        64
    hrong  
       2016-06-30 21:17:30 +08:00 via Android
    表驱动,策略模式。

    有人说什么反射,想想反射里面有多少个 if/else 就知道用反射有多么的得不偿失
    cocoaChina
        65
    cocoaChina  
       2016-06-30 21:20:30 +08:00 via Android
    这种判断逻辑必须有,但逻辑内实现却可以瘦身,尽量多用 switch
    powergx
        66
    powergx  
       2016-06-30 21:54:57 +08:00 via iPhone   ❤️ 1
    cpu 晶体管可是只有通河短两种情况, lz 竟然说不用 if
    rashawn
        67
    rashawn  
       2016-06-30 22:16:46 +08:00
    最下面都是一样的吧 不用这个是不是能跑的快一点
    cheng4741
        68
    cheng4741  
       2016-06-30 22:55:42 +08:00
    @EAimTY 并不会,这种情况用表或数组的话,再底层也不会有 if else
    aias
        69
    aias  
       2016-06-30 23:20:13 +08:00
    @mygirl Hello ,宁波的同学,又看到你了!招人还顺吗?
    peneazy
        70
    peneazy  
       2016-06-30 23:23:27 +08:00 via Android
    我只知道 js 怎么消除这种东西。利用多态性,比如原型继承,把每一种可能封装在原型里,构造函数直接调用。看看设计模式吧,有至少两种模式能大幅度减少判断语句。
    holy_sin
        71
    holy_sin  
       2016-06-30 23:41:09 +08:00
    问题的核心就是区分出不同类型的 cell 要如何渲染。如果在 vc 里做,那么 collectionView 对应的所有 datasource 和 delegate 都需要分别写一套区分方法。所以比较好的方法是在构造数据源的时候,指定好每一种 cell 如何显示。由于 cell 信息可能会比较多,所以就衍生出来用 cellViewModel 来管理这些信息,这就是数据驱动的意思吧。当 cell 的排列关系需要改动的时候,只需要修改构造数据源的地方。其实就是 @lingoerer 所说的做法。
    hinkal
        72
    hinkal  
       2016-06-30 23:46:59 +08:00
    利用多态减少 if else 才是正解,否则说明上下文中 if else 不应该被消除。
    jackisnotspirate
        73
    jackisnotspirate  
       2016-06-30 23:57:51 +08:00 via iPad   ❤️ 1
    tutuge
        74
    tutuge  
       2016-07-01 00:02:13 +08:00
    模板方法解决, cell 基类,传入 index 等参数,让 cell 不同子类自己处理。也就是多态=。=
    yangff
        75
    yangff  
       2016-07-01 01:24:05 +08:00 via Android
    @zhuangzhuang1988 讲道理 switch 本来就是 jump table ,编译器还会把它塞到 ro 的段上,安全性还更好些
    arakashic
        76
    arakashic  
       2016-07-01 02:14:40 +08:00
    @yangff 实际上编译器并不总是把 switch 生成成 jump table 。
    wangyifei6817
        77
    wangyifei6817  
       2016-07-01 09:22:04 +08:00
    这里不谢 也要写到别的地方 换个地方恶心而已
    miaotaizi
        78
    miaotaizi  
       2016-07-01 09:32:05 +08:00
    策略模式?
    wander2008
        79
    wander2008  
       2016-07-01 10:19:31 +08:00 via iPhone
    @hyyy 是的,逻辑层面确实不行。
    搞设计模式可以。 java
    wmhx
        80
    wmhx  
       2016-07-01 10:54:39 +08:00
    java 的工厂模式. 参见 jfinal
    eimsteim
        81
    eimsteim  
       2016-07-01 11:13:27 +08:00
    @ihuotui 这跟责任链有毛线关系,学设计模式都学傻了吧
    coa
        82
    coa  
       2016-07-01 11:57:52 +08:00
    @lingoerer 学习了。。请教下这种是不是得把所有 ViewModel 都放同一数组了,不同 Cell 连续存放倒还好办,如果是交叉着放还能用这办法吗?
    yangff
        83
    yangff  
       2016-07-01 12:12:00 +08:00
    @arakashic 编译器会选择合理的方式来生成代码
    bobuick
        84
    bobuick  
       2016-07-01 12:12:02 +08:00
    模式匹配吧, rust 或 haskell 那种模式匹配方式,不过完全杜绝是不可能的, 不过可以大部分解决了
    nozama
        85
    nozama  
       2016-07-01 21:23:14 +08:00 via iPhone
    这种情况 mvvm 吧, datasource 里面只存放一堆抽象的 cellViewModel ,并通过工厂来获得具体的 cell ,工厂可以用数据来驱动。
    Balthild
        86
    Balthild  
       2016-07-01 22:17:23 +08:00
    全部用 goto
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5888 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 02:47 · PVG 10:47 · LAX 18:47 · JFK 21:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.