举个例子,在 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 时会使代码非常多,求大神们能不能给个思路,遇到这种场景尽量不要出现这种嵌套代码,最好给个实例膜拜膜拜 ^_^
1
wander2008 2016-06-30 10:59:19 +08:00 via iPhone
不可能
|
2
ourcubk 2016-06-30 10:59:51 +08:00
不可能
|
3
chairuosen 2016-06-30 11:02:33 +08:00
switch 可以打表,简单 if 也可以
|
4
zhuangzhuang1988 2016-06-30 11:02:49 +08:00 4
1. 表驱动法(看: 代码大全)
2. 使用 match(看: F#, Scala)' 3. 使用类+继承(看: 设计模式) |
5
Jabin 2016-06-30 11:03:11 +08:00
封装 工厂
|
6
ipconfiger 2016-06-30 11:06:31 +08:00 1
该用则用, if else 是基础逻辑, 如果不是深层次嵌套的话, 最好不要作一些奇技淫巧出来
|
7
hyyy OP @wander2008
其实我觉得这是一个有趣的话题^_^ |
8
kera0a 2016-06-30 11:10:24 +08:00
|
9
hasbug 2016-06-30 11:14:16 +08:00
这能避免吗? 除非不做判断···
|
10
stcasshern 2016-06-30 11:16:05 +08:00
这个不能吧,印象中这个是最基础得
|
11
ma125125t 2016-06-30 11:19:24 +08:00
目前的 tableView 就是这么艹蛋。但已经有很多开源的方法解决这个问题了(想象一下一百种 cell ,分为数十个 section 和 row 的情况)。去找找吧。
|
12
michaelye1988 2016-06-30 11:23:45 +08:00 2
我认为这种情况下用 if else 或者 switch 是非常合理的,至少代码可读性还是在的。如果非要套用一些设计模式什么的,维护起来就痛苦了。
|
13
chmlai 2016-06-30 11:24:15 +08:00
这个简单, 用数据驱动就清晰多了
|
14
spongebobsun 2016-06-30 11:24:39 +08:00
@kera0a 学习了
|
16
chunqiuyiyu 2016-06-30 11:27:11 +08:00
完美消除是不可能的,不然这几个关键字就不会出现。重要的是理清逻辑,不要嵌套过深。
|
17
rekulas 2016-06-30 11:27:43 +08:00
看不懂 两种都是返回 cell 条件为什么不整合在一起
|
18
8bit 2016-06-30 11:31:19 +08:00 via Android
查表,代码大全
|
19
subpo 2016-06-30 11:34:25 +08:00
能只写 if 不写 else 不错了
|
20
harry890829 2016-06-30 11:37:08 +08:00
这种平级的 if...else...已经不错了,我刚接手了一个代码,到手的时候 4-5 层 if 结构嵌套,全都带 else ,因为需求的关系,我现在又在里面加了两层 if..else...结构,我已经恶心死了
|
21
woshicixide 2016-06-30 11:39:50 +08:00 via Android
多态
|
22
jason19659 2016-06-30 11:42:46 +08:00
cell = indexPath.section == 3 ? a : indexPath.row == 2 ? b : c
|
23
zhicheng 2016-06-30 11:46:00 +08:00
静态结构这样写没问题,把 3 和 2 消除就行,用 USER_SECTION 常量定义一下。
动态结构用 model 来判断。 |
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/ |
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 的数组 |
26
ibigbug 2016-06-30 12:31:32 +08:00
|
27
muller 2016-06-30 12:34:48 +08:00
策略模式
|
28
davisz 2016-06-30 12:36:02 +08:00
do {
if (false) break; }while(0); |
29
ilotuo 2016-06-30 12:37:31 +08:00
这样?
```java cell = indexPath.section == 3 ? CellFactory.build(...) : indexPath.row == 2 ? CellFactory.build(...): null; ``` |
30
xwartz 2016-06-30 13:17:36 +08:00
map
|
31
mdluo 2016-06-30 13:24:43 +08:00
分支存数组,条件用位运算
|
32
ihuotui 2016-06-30 13:31:24 +08:00 via Android
责任连模式,差不多
|
33
hantsy 2016-06-30 13:33:01 +08:00
State 模式
|
34
twoyuan 2016-06-30 13:42:16 +08:00
首先应该消灭的不是 Magic Number 吗……
|
35
sutra 2016-06-30 13:46:55 +08:00
Map
|
36
youyongsong 2016-06-30 14:00:32 +08:00
pattern match
|
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 } |
38
JasperYanky 2016-06-30 14:02:42 +08:00
model 决定 view controller 层不关心数据 只负责传递
|
39
fhefh 2016-06-30 14:06:15 +08:00
|
40
loveuqian 2016-06-30 14:09:07 +08:00
之前也是这样判断行数,后来改判断模型 title 了
因为判断行数,一不小心,在第 0 行前面加一行,你这所有判断都要重写啊。。 |
41
vincentxue 2016-06-30 14:13:31 +08:00
看来 iOS 迫切需要一个为 UITableView 或者 UICollectionView 解决数据源封装的库。
|
42
sensui7 2016-06-30 14:22:34 +08:00
OO 中的 tell, don't ask 原则可以消除这种大量的 else , if else 多说明代码抽象的不好, 根据你的逻辑,可以采用相应的设计模式解决
|
43
lalalafq 2016-06-30 14:29:03 +08:00
所有的数据源逻辑(高度,点击,渲染)全部丢到一个 array ;基础的 tableView 委托永远只有大概 10 行左右;
绝壁高聚合,低耦合;产品想怎么改就怎么改;顺序啥的都是小问题。 |
44
JohnSmith 2016-06-30 14:33:48 +08:00
rust 中有个 match
|
45
skyoojaa 2016-06-30 15:05:24 +08:00 via Android
If...else...没法消除,但可以封装判断,让 if...else...里面的代码看起来简洁一些
|
46
realpg 2016-06-30 15:22:54 +08:00
我觉得这种代码很好
瞎搞弄得可维护性差才是大坑 |
47
iyeatse 2016-06-30 15:40:35 +08:00
|
49
so898 2016-06-30 15:56:52 +08:00
真要很多的话不应该是自定义 Cell ,然后把代码放到 Cell 里面去么?
如果说不能自定义 Cell ,那不是应该通过在外面建立 Cell 内容填充生成的方法来做到精简这一块的代码么? |
50
missdeer 2016-06-30 16:38:21 +08:00
你看,用 OO 设计模式的时候到了,上面说不行的都不是那个年代过来的人
|
51
weirdyu 2016-06-30 16:53:45 +08:00
你这种按照行数来判断返回何种 cell 的方式不太好
|
52
zjyjer 2016-06-30 17:05:44 +08:00
1. 模式匹配
2. 设计一个状态机 每个分支里的代码抽象出一个函数异步调用之 |
53
programgou 2016-06-30 17:11:55 +08:00
我认为我们可以专门提供一个 indexPath 的接口,然后再提供一个 builder ,把实现了 indexPath 接口的类的实例传递给 builder , builder 就可以返回一个 cell 。这样无论你有多少种 indexPath ,只需要维护 builder ,而不会改变 tableView 的任何代码。当你觉得你现在的 builder 过于复杂的时候,你甚至可以为这个 builder 提供接口,这样你可以在不改变原来代码的基础上,替换掉原来的 builder 。总之,类与类建立关系要通过接口,这样就可以在不改变代码的基础上,换掉一个类。也就是换掉一个功能。
这里面涉及到一些设计模式的东西,不过楼主别怕,看不懂书建议去看源码,我博客上写过一个关于建构者模式的分析,你可以参考一下,不过要解决你的问题一个建构者模式肯定是不够的,还有工厂模式。 |
54
sablib 2016-06-30 17:13:05 +08:00
如果是 swift 的话,就用 enum 了。
|
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)这种方式调用函数。 |
56
chrisstyle 2016-06-30 17:36:22 +08:00
同 25 楼~
推荐 |
57
i4mszengg 2016-06-30 18:02:04 +08:00
我想应该不用故意写一些奇葩的逻辑,日后维护很痛苦的吧,如果 单个 if 的 condiftions 比较多,可以嵌套 if , 这样更容易懂些,日后好维护。
|
58
jasonlz 2016-06-30 18:25:42 +08:00
用状态机。
|
60
djyde 2016-06-30 18:34:00 +08:00
程序之所以是程序,因为它能 if else
|
61
EAimTY 2016-06-30 18:37:28 +08:00 via Android
不可能的,不论用什么方法代替,底层肯定还会是 if else
|
62
adrianzhang 2016-06-30 18:50:00 +08:00 via Android
结构化语言之前,都用 goto ,汇编里见 jump 更多。
|
63
xwing 2016-06-30 20:06:43 +08:00
命令链模式。
|
64
hrong 2016-06-30 21:17:30 +08:00 via Android
表驱动,策略模式。
有人说什么反射,想想反射里面有多少个 if/else 就知道用反射有多么的得不偿失 |
65
cocoaChina 2016-06-30 21:20:30 +08:00 via Android
这种判断逻辑必须有,但逻辑内实现却可以瘦身,尽量多用 switch
|
66
powergx 2016-06-30 21:54:57 +08:00 via iPhone 1
cpu 晶体管可是只有通河短两种情况, lz 竟然说不用 if
|
67
rashawn 2016-06-30 22:16:46 +08:00
最下面都是一样的吧 不用这个是不是能跑的快一点
|
70
peneazy 2016-06-30 23:23:27 +08:00 via Android
我只知道 js 怎么消除这种东西。利用多态性,比如原型继承,把每一种可能封装在原型里,构造函数直接调用。看看设计模式吧,有至少两种模式能大幅度减少判断语句。
|
71
holy_sin 2016-06-30 23:41:09 +08:00
问题的核心就是区分出不同类型的 cell 要如何渲染。如果在 vc 里做,那么 collectionView 对应的所有 datasource 和 delegate 都需要分别写一套区分方法。所以比较好的方法是在构造数据源的时候,指定好每一种 cell 如何显示。由于 cell 信息可能会比较多,所以就衍生出来用 cellViewModel 来管理这些信息,这就是数据驱动的意思吧。当 cell 的排列关系需要改动的时候,只需要修改构造数据源的地方。其实就是 @lingoerer 所说的做法。
|
72
hinkal 2016-06-30 23:46:59 +08:00
利用多态减少 if else 才是正解,否则说明上下文中 if else 不应该被消除。
|
73
jackisnotspirate 2016-06-30 23:57:51 +08:00 via iPad 1
|
74
tutuge 2016-07-01 00:02:13 +08:00
模板方法解决, cell 基类,传入 index 等参数,让 cell 不同子类自己处理。也就是多态=。=
|
75
yangff 2016-07-01 01:24:05 +08:00 via Android
@zhuangzhuang1988 讲道理 switch 本来就是 jump table ,编译器还会把它塞到 ro 的段上,安全性还更好些
|
77
wangyifei6817 2016-07-01 09:22:04 +08:00
这里不谢 也要写到别的地方 换个地方恶心而已
|
78
miaotaizi 2016-07-01 09:32:05 +08:00
策略模式?
|
79
wander2008 2016-07-01 10:19:31 +08:00 via iPhone
@hyyy 是的,逻辑层面确实不行。
搞设计模式可以。 java |
80
wmhx 2016-07-01 10:54:39 +08:00
java 的工厂模式. 参见 jfinal
|
82
coa 2016-07-01 11:57:52 +08:00
@lingoerer 学习了。。请教下这种是不是得把所有 ViewModel 都放同一数组了,不同 Cell 连续存放倒还好办,如果是交叉着放还能用这办法吗?
|
84
bobuick 2016-07-01 12:12:02 +08:00
模式匹配吧, rust 或 haskell 那种模式匹配方式,不过完全杜绝是不可能的, 不过可以大部分解决了
|
85
nozama 2016-07-01 21:23:14 +08:00 via iPhone
这种情况 mvvm 吧, datasource 里面只存放一堆抽象的 cellViewModel ,并通过工厂来获得具体的 cell ,工厂可以用数据来驱动。
|
86
Balthild 2016-07-01 22:17:23 +08:00
全部用 goto
|