代码中的if…else杂谈1

if-else过多的部分问题分析。

问题

很多编码规范/设计模式/重构方面的书、很多公司的编码规范都提到过if-else嵌套过深/if-else过多这些个“坏味道”,公司的或从学校出来的”屎山代码”或多或少有这些味道,形如:

问题1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if(...)
{
if(...)
{
if(...)
{

}
}
}
else
{
if(...)
{
if(...)
{

}
}
}

这种类型的if-else应该是最应该避免的,因为这么写的问题最多:

  1. 维护困难:当嵌套层数太深后,想搞明白整段代码相当困难;
  2. 单元测试困难:需要的测试用例太多,很难覆盖所有情况;
  3. 误导“后浪”:坏的习惯会传染人,特别是当新人维护这段代码时很容易就把这种不好的习惯学走;就算是老人维护,在时间紧张或怕无法理清楚整体逻辑时,只能继续嵌套更多if-else,让代码“莫名其妙的”正确运行起来。
  4. 影响预读和cache命中:两年前面试时被问到了这个问题,当时只提到了前面几点,后来才想起来面试官最想要这个答案,愧对我的导师兼《微机原理》课老师。CPU都是多级流水线架构运行,如果分支预测成功,很多指令都提前进入流水线流程中,则流水线中指令运行的非常顺畅,而如果分支预测失败,则需要清空流水线中的那些预测出来的指令,重新加载正确的指令到流水线中执行,然而现代CPU的流水线级数非常长,分支预测失败会损失10-20个左右的时钟周期,因此对于复杂的流水线,好的分支预测方法非常重要。(引自if-else的效率有多低你知道吗?-电子发烧友网 (elecfans.com);

又如:

问题2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if(...)
{
...//重复一堆
type = u8"狗";
...//重复一堆
legs = 4;
run(type, legs);
...//其他重复一堆
}
else if(...)
{
...//重复一堆
type = u8"海鸥";
...//重复一堆
legs = 2;
run(type, legs);
...//其他重复一堆
}

...

else
{
...//重复一堆
type = u8"螃蟹";
...//重复一堆
legs = 8;
run(type, legs);
...//其他重复一堆
}
  1. 个人感觉这种链式的if-else可以接受;
  2. 但是这种写法经常会伴随着大量的复制-粘贴,这就有“坏味道”了。
  3. switch-case也有同样的问题。

更有甚者:

问题3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool b1; //(就不初始化 (¬、¬))
bool b2;
bool b3;
bool b4;
bool b5;
... //省略同一函数中1024条代码
b1 = true;
...
b2 = false;
b3 = true;
... //省略同一函数中又1024条代码
b4 = false;
b5 = true;

if(b1 && b2 || !b3 && b4 || b5)
{

}
else if(!b1 || b2 && !b3 && b4 || b5)
{

}
...//省略10086种情况
else
{

}

学校出来的代码不少这么写的,丝毫没有夸张成分。这已经不仅仅是if-else多的问题了,还有个滥用标志位的问题:

  1. 标志位不初始化;
  2. RadioButton的按下状态需要标志位记录;
  3. vector::empty()是否为true需要标志位记录;
  4. ComboBox是否选择第一行需要标志位记录;
  5. ……

类大、函数长

(假如我的一个函数能实现5个功能,一个类有50个这种函数,那么我这个类就可以实现250个功能, 我真是个小机灵鬼(¬、¬))

标志位多、if-else一长串,维护起来相当困难。

总结

个人感觉if-else既不是洪水猛兽,也不能放任发展。不能要求每个人都会设计模式,况且大佬们对是否应该使用设计模式都意见不一。日常编码中注意最常见的问题即可,没必要反if-else反到“洁癖”。。

参考


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!