程序中减少使用if语句的方法集锦

大约十年前,我听说了反if的活动,觉得这个概念非常荒谬。如果不用if语句,又怎么能写出有用的程序呢?这简直太荒谬了。

但之后你会开始思考:是否还记得上周你拼命想读懂的深度嵌套代码?糟透了对么?要是有办法能简化它该多好。

反if活动的网站上没给出多少实用性建议,因此在本文中,作者将会提供一系列模式,也许你会用得上。但首先我们来关注一下if语句到底造成了什么问题。

if语句的问题

if语句的第一个问题在于,通常出现if语句的代码很容易越改越糟。我们试着写个新的if语句:

这时候还不算太糟,但已经存在一些问题了。在阅读这段代码时,我必须得去查看对同一个SharedState来说,CodeBlockA和CodeBlockB有什么改动。最开始这段代码很容易阅读,但随着CodeBlock越来越多,耦合越来越复杂之后,就会很难读。

上面这种CodeBlock进一步嵌套if语句与本地return的滥用情况也很常见,很难搞懂业务逻辑是选择了哪种路径。

if语句的第二个问题在于:复制时会有问题,也就是说,if语句缺失domain的概念。很容易由于在不需要的情况下,由于将内容放在一起而增加耦合性,造成代码难读难改。

而第三个问题在于:开发者必须在头脑中模拟执行实现情况——你得让自己变成一台小型电脑,从而造成脑细胞浪费。开发者的精力应当用来思考如何解决问题,而不是浪费在如何将复杂的代码分支结构编织在一起之上。

虽然想要直截了当地写出替代方案,但首先我得强调这句话:

凡事中庸而行,尤其是中庸本身

if语句通常会让代码更加复杂,但这不代表我们要完全抛弃if语句。我曾经看到过一些非常糟糕的代码,只是为了消除所有的if语句而刻意避开if语句。我们想要绕开这个误区, 下面我给出的每种模式,都会给出使用范围。

单独的if语句如果不复制到其他地方,也许是不错的句子。在复制if语句时,我们会希望预知危险的第六感起效。

在代码库之外,在与危险的外部世界交流时,我们会想要验证incoming response,并根据其作出相应的修改。但在自己的代码库中,由于有可靠的gatekeeper把关,我觉得这是个很好的机会,我们可以尝试使用简单、更为丰富与强大的替代方案来实现。

模式1:布尔参数(Boolean Params)

背景: 有方法在修改行为时使用了boolean。

问题: 在看到这段代码时,实际上你是将两个方法捆绑到一起,布尔参数的出现让你有机会在代码中定义一个概念。

适用范围: 通常看到这种情况,如果在编译时我们可以算出代码要采用哪种路径,就可以放心使用这种模式。

解决方案: 将这个方法拆分成两个新的方法,然后if就不见了。

模式2:使用多态(Polymorphism)

背景: 根据类型switch时。

问题: 在添加新的类型时,我们必须要记得更新switch语句,此外随着不同bird的概念添加进来,bird类的凝聚力越来越糟。

适用范围:根据类型做单次切换是可行的,如果switch太多,在添加新类型时如果忘记更新现有隐藏类型中的所有switch,就会导致bug出现。

解决方案: 使用多态,添加新类型时大家都不会忘记添加相关行为。 注意:上例为了简洁只写了一个方法,但在有多个switch时更有用。

模式3:NullObject/Optional

背景: 当外部请求理解代码库的主要用途时,回答“查一下null的情况”。

模式4:将内联语句(Inline statements)转为表达式

背景: 在计算布尔表达式时,包含if语句树。

问题: 这种代码会导致开发者必须用大脑来模拟计算机对方法的处理。

适用范围:很少有不适用的情况,像这样的代码可以合成一行,或者拆成不同的部分。

解决方案: 将if语句树合成单个表达式。

模式5:给出应对策略

背景:在调用一些其他代码时,无法确保路径是成功的。

问题: 这类if语句增加了处理同一个对象或者数据结构的时间,其中包含隐藏耦合——null的情况。其它对象可能会返回其他代表没有结果的Magic value。

适用范围:最好将这类if语句放在一个地方,由于不会重复,我们就能将为空对象的magic value删除。

解决方案:针对被调用代码,给出应对策略。Ruby的Hash#fetch就是很好的案例,Java也用到了类似的方法。这种模式也可以用在删除例外情况时。

祝探索愉快

希望这些模式对你现在处理的问题有帮助。我在重构代码增进理解时,发现这些方法都很有用。要记得并非所有if语句都是魔鬼,不过现代编程语言还有很多功能值得我们探索并使用。

原文发布于微信公众号 - java一日一条(mjx_java)

原文发表时间:2016-07-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏转载gongluck的CSDN博客

黑暗的内存管理

黑暗的内存管理 很多人对 C 语言深恶痛绝,仅仅是因为 C 语言迫使他们在编程中必须手动分配与释放内存,然后通过指针去访问,稍有不慎可能就会导...

4026
来自专栏idealclover的填坑日常

C语言循环和switch中的break和continue

问题的关键在于循环和switch中的break和continue的不同。在switch中是响应break但不响应continue的,换句话说,在switch中使...

1281
来自专栏PHP在线

PHP 代码规范简洁之道

原文出处: Scholer 1. 统一的编码规范 编码规范往简单说其实就是三个方面: 换行 空格 变量命名 放在 PHP 里面,还有一些附加的地方,比如关键字...

3986
来自专栏小樱的经验随笔

【请您听我说】PHP语法特点的一些看法

一、基本认识   PHP是干什么的?百度百科上提到说:PHP就是一门脚本语言,开发用的,相信这个你们只要去搜一下,就会有一大堆关于PHP概念的解释。   相信我...

3556
来自专栏从流域到海域

Python 条件判断

Python if语句 格式:注意不要漏写:这是容易犯错的地方。 if语句包含两部分,判断部分和执行部分,只有在判断部分得到的布尔值为True时,才会执行执...

23210
来自专栏代码世界

6大设计原则总结

6大设计原则总结 一、单一职责原则  单一职责原则:英文名称是Single Responsiblity Principle,简称是SRP。定义:应该有且仅有一个...

2809
来自专栏Java技术栈

6 道 BATJ 必考的 Java 面试题

请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别?

1041
来自专栏java一日一条

关于 Java 的10个谎言

下面的这些都算是比较高级的问题了,面试中一般也很少问到,因为它们可能会把面试者拒之门外。不过你可以自己找个时间来实践一下。

631
来自专栏Vamei实验室

Java基础05 实施接口

在封装与接口中,private关键字封装了对象的内部成员。经过封装,产品隐藏了内部细节,只提供给用户接口(interface)。 接口是非常有用的概念,可以辅助...

2147
来自专栏章鱼的慢慢技术路

2015年第六届蓝桥杯C/C++B组省赛题目解析

2262

扫码关注云+社区