如何完美的消除 if...else..或者 switch 这种代码,求大神给个思路。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
hyyy
V2EX    程序员

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

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

    举个例子,在 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
    这个简单, 用数据驱动就清晰多了
        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  
    PRO
       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  
    PRO
       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     Solana     5517 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 48ms UTC 03:36 PVG 11:36 LAX 20:36 JFK 23:36
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86