大家觉得在接手遗留代码时,见到什么东东是最让人感到不耐烦的?复杂无比的 UML ?我觉得不是。我的答案是,超过两个 else 的 if ,或者是超过两个 case 的 switch 。可是在代码中大量使用 if else 和 switch case 是很正常的事情吧?错!绝大多数分支超过两个的 if else 和 switch case 都不应该以硬编码( hard-coded )的形式出现。
复杂分支从何而来
首先我们要讨论的第一个问题是,为什么遗留代码里面往往有那么多复杂分支。这些复杂分支在代码的首个版本中往往是不存在的,假设做设计的人还是有点经验的话,他应该预见将来可能需要进行扩展的地方,并且预留抽象接口。
但是代码经过若干个版本的迭代以后,尤其是经过若干次需求细节的调整以后,复杂分支就会出现了。需求的细节调整,往往不会反映到 UML 上,而会直接反映到代码上。例如说,原本消息分为聊天消息和系统消息两类,设计的时候自然会把这设计为消息类的两个子类。但接着有一天需求发生细节调整了,系统消息里面有一部分是重要的,它们的标题要显示为红色,这时候程序员往往会做如下修改:
在系统消息类上面加一个 important 属性
在相应的 render 方法里面加入一个关于 important 属性的分支,用于控制标题颜色
程序员为什么会作出这样的修改?有可能因为他没意识到应该抽象。因为需求说的是「系统消息里面有一部分是重要的」,对于接受命令式编程语言训练比较多的程序员来说,他或许首先想到的是标志位──一个标志位就可以区分重要跟不重要。他没想到这个需求可以用另一种方式来解读,「系统消息分为重要和不重要两种类别」。这样子解读,他就知道应该对系统消息进行抽象了。
当然也有可能,程序员知道可以抽象,但基于某些原因,他选择了不这样做。很常见的一种情况就是有人逼着程序员,以牺牲代码质量来换取项目进展速度──加入一个属性和一个分支,远比抽象重构要简单得多,如果要做10个这种形式的修改,是做10个分支快还是做10个抽象快?区别显而易见。
当然, if else 多了,就有聪明人站出来说「不如我们改成 switch case 」吧。在某些情况下,这确实能够提升代码可读性,假设每一个分支都是互斥的话。但是当 switch case 的数量也多起来以后,代码一样会变得不可读。
复杂分支有何坏处
复杂分支有什么坏处?让我从百度 Hi 网页版的老代码里面截取一段出来做个例子。
switch (json.result) {
case "ok":
switch (json.command) {
case "message":
case "systemmessage":
if (json.content.from == ""
&& json.content.content == "kicked") {
/* disconnect */
} else if (json.command == "systemmessage"
|| json.content.type == "sysmsg") {
/* render system message */
} else {
/* render chat message */
}
break;
}
break;
出处:百度泛用户体验