译者:白玉堂

作者:Jason McCreary

原文:jasonmccreary.me/articles/un…

我不相信写代码的硬性规则,但是你经常能听到。比如一个方法不应该超过15行,方法只应该有一个 return 语句,缩进必须是4个空格等等。这些规则太死板了。
实际代码要灵活的多。这些硬性规则带来的副作用是让我们偏离了实质 可读性。如果我的关注点完全放在行数或 return 语句,这只会阻碍自己写高可读性代码,因为它们有不少都“太长”或多个 return 语句。
很多这些硬性规则试图解决嵌套代码问题。嵌套代码很难跟进逻辑去理解。感官上,这样更需要用眼睛扫描更多;心理上,每个嵌套层级都需要付出更多精力跟踪理解功能,这些都会让阅读者费劲。
嵌套代码主要是条件判断的结果。自从条件语句成为编程逻辑的基础,我们不能很好的移除它们。我们必须识别出他们对读者的影响,采取措施减少这种影响。

回到顶部

为了提高可读性,我们想让代码回到顶部。循环和条件语句天生就有一个嵌套结构,在这些代码块中无法避免的要嵌套。然而,我们可以避免在此结构之外的嵌套。
我们一起看几个嵌套代码的例子来练习一下提高他们的可读性。

空代码块

可能你不相信我,但是我不止一次的看到这样的代码:

<?phppublic function handle($request, Closure $next)
{if (env('APP_ENV') == 'development') {// do nothing...} else {if ($request->getScheme() != 'https') {URL::forceScheme('https');return redirect('https://www.example.com/' . $request->getPath());}}return $next($request);
}
复制代码

就是这,一个空的 if 语句块,我还看到过另一面:一个空的 else 代码块。没有规定一个 if 必须和一个 else 成对出现。至少在过去的 20 多年没有任何一门编程语言规定这样。空代码块是死代码,删除他们。

条件值

嵌套代码经常会返回一个值。如果值是 boolean 型,这是压缩代码的机会,直接 return 掉条件判断。
考虑一下这个 Set 类中 isEmpty 方法的嵌套代码:

<?phppublic function isEmpty() {if ($this->size === 0) {return true;} else {return false;}
}
复制代码

尽管这个方法块只有 4 行代码,还包括多个子块。即使这么少的代码行数,也难于阅读,因为它让代码比它实际要出现更高的复杂度。
通过识别原始 boolean 值的条件返回,我们有很好的机会通过直接返回条件完全删除嵌套代码。

<?phppublic function isEmpty() {return $this->size === 0;
}
复制代码

结合这个恰当的方法命名和现在的一行代码块的上下文,我们降低了代码的理解复杂度。尽管这行代码可能会比较密集,但它仍然比重构前更具可读性。
注:压缩条件判断可以适用于很多数据类型不仅限于布尔类型。例如,你可以使用简单类型的整型作为条件返回。然后,这也会迅速的增加了复杂度。很多的编程者尝试使用三目运算来解决这种情况。但是三目运算能够节省代码却没有降低复杂度,还会降低代码可读性。在这种情况下,卫语句是更好的选择。

卫语句

嵌套代码经常是逻辑层次递进的结果。我们作为编程者,要写出每一个条件直到可以安全操作的程度。
但是这个流程对程序执行来说是典范,对代码阅读却不是。因为每一层代码嵌套,代码阅读者必须保持一种持续增加的思维模式。
细想下面的 Setadd 方法的实现:

<?phppublic function add($item) {if ($item !== null) {if (!$this->contains($item)) {$this->items[] = $item;}}
}
复制代码

代码逻辑是这样:如果item 不是 null 并且 如果 Set 类不包含 item,就把 item 添加到数组里。问题是:这不仅这么简单的操作让人感到复杂,更让主要操作埋藏在最深层的代码里。
理想状态下,代码的主要操作位于顶部。我们可以把条件语句重构成卫语句,达到解开嵌套语句和突出主要操作的目的。
卫语句只是想保护我们的方法不受一场路径的干扰。尽管他们通常初夏你在代码块的顶部,他们也可以出现在任何地方。我们可以运用 De Morgan's Laws 把任何的嵌套条件转换成卫语句并放弃层层的控制。代码里,这意味着我们不用条件语句,并采用 return语句。
把以上思路应用到  add 方法,我们的实现如下:

<?phppublic function add($item) {if ($item === null || $this->contains($item)) {return;}$this->items[] = $item;
}
复制代码

译者注:其实卫语句也运用了《重构》里经常提到的尽早返回的思想,把异常情况直接打回去
这么做,我们不仅提炼了主逻辑,还突显出方法的异常路径。现在对未来的代码阅读者少了一些复杂度,这也让被突显出的异常情况更易于测试。

“切换”到 if

switch 语句是一个非常啰嗦的语法结构,switch 有固定的 4 个关键字和三个级别。即使代码块里只有几行代码仍然要阅读很多代码。虽然者在某些情况下可以接受,但在其他情况下并不是。
有的情况使用 if 语句来替代 switch 语句可能会产生更多较高可读性代码。

  • 当只有几个 case 代码块时,switch 语句的固定结构却比等效的 if 语句产生更多行代码。
  • 当 case 代码块包含嵌套增加了复杂度降低了可读性到达危险的水平,使用卫语句或大代码块的实践方法能够改进代码。
  • 当类型转换或计算需要引导值到 swtich 约束时,这不适用于不支持复杂的 case 比较的编程语言(如:swift,go 等)

switch 语句非常适用于 1 :1 的已有 case 语句并且有他们自己多行的代码块场景。无论这些代码行是赋值,return 语句或者方法调用,比率大致为 1 :1 则可读性几乎保持不变。

<?phpswitch ($command) {case 'action':startRecording();break;case 'cut':stopRecording();break;case 'lights':adjustLighting();break;
}
复制代码

注:当 switch 语句是流线型的情况下,很多编程者使用字典/数据表,或多态代替。所有这些却是其他的替代品。就记住每一个方案都有权衡(复杂性),对于大多数代码,switch 语句通常“足够好”。

复杂的循环

另一个嵌套的表现是循环,循环具有天然的复杂度。作为一个编程者,我们就像被一个人诅咒了一样,总想...增量逻辑。同样,作为人类,我们不是计算机,我们不太可能赢得了和计算机的循环计算。计算机会一直比人类强大,能和复杂唯一与之抗衡的是可读性。
我不会介绍可能会改进你代码的数据结构或算法。那比较有特定性。通常,大多数循环处理累加或调用。如果你发现你的代码库包含太多循环,看下是否有类似 filter / map / reduce 等的高阶函数被使用。虽然这可能无法帮助所有的阅读代码者改进可读性,但它会提高你的个人技能。

译者后记

我常常在想,我们为什么要和代码做斗争?
1、代码没有错,代码构建的软件帮助公司创造价值,争取市场份额,创造了收益。
2、软件随着功能和需求的迭代,原始的架构设计肯定不能满足后续的变更,结果造成代码臃肿,维护成本增加,软件风险增加,也间接加剧了产品的风险,甚至对公司营收都会造成风险和影响。
3、软件的腐烂不可避免,我们能做的是延缓这种腐烂,不断重构我们的代码,重构不一定是整个接口或功能全部推倒重来,也可以是一个函数的优化,一行代码的优化。这些小的动作都能降低代码腐烂的速度,降低bug数,降低维护成本和扩展成本。于公,可以为公司节省开支;与私,开发也能早点下班;于职业规划,好的代码也是一个程序员的门面,对自己的代码质量和编程习惯负责也是以后求职的一个竞争优势。

基于原作者的分享,我也分享我看到的,在代码编写初期就能改进的,(如果代码编写完成了,再去重构,代价会更大),这些小动作就和“勿以善小而不为”一个道理,积少成多,我们每天都能写出比昨天更好的代码。

多余的变量

<?php
class Do
{public $api_url = 'http://www.domain.com/api/name/action';public function requestRemoteApi(array $params){$url = $this->api_url;// ... do someting$res =  HttpHelper::post($url,$data);return $res;}
}
复制代码

比如这里有两点:

  1. 类已经有一个全局变量 $api_url,在函数内部又重新赋值给一个局部变量,这个是完全没必要的。
  2. 响应如数据赋值给变量 $res,却没有做任何处理,再后边才返回这也是没有必要的,除非你要拿到结果做判断和处理,否则单独赋值给一个新的变量是多余的。

直接写

<?php
class Do
{public $api_url = 'http://www.domain.com/api/name/action';public function requestRemoteApi(array $params){// ... do sometingreturn  HttpHelper::post($this->api_url,$data);}
}
复制代码

直接的复制粘贴

在代码优化的原则里的确是有一个“延迟决定”的思想,就是说当我们做代码抽象的时候,一次是特例,两次是偶然,三次你就需要考虑抽象封装了。
但是在代码里我们往往看到完完全全的复制,只修改了一些简单的一行配置或者 变量名不同,更有甚者惧于生产环境风险直接复制了一个新的文件,甚至一个包含一千多个文件的文件夹,命名后缀只是简单的加了数字1/2/3/4 等等 folderName1 , folderName2 , folderName3 。

  • 我们始终要记得:代码是给人看的,并且不只是写给自己,是写给自己小伙伴。
  • 写好代码,写漂亮代码,是对自己的负责,对同事和团队的负责,更是对公司的负责。
  • 重构越早,代价越小

扩展阅读

软件的高质量意味着高成本?
技术债

[译文]解开嵌套代码相关推荐

  1. 嵌套访问_利用Idea重构功能及Java8语法特性——优化深层嵌套代码

    当遇到深层嵌套代码,如for,if,lambda表达式或内部类及这些代码的组合,这时我们可以通过Java 8的语法特性来进行优化. 下面的代码是一个嵌套循环的示例. public MappedFiel ...

  2. python多个if_Python之条件判断/if嵌套/如何写嵌套代码

    一.条件判断 条件判断是计算机沟通的逻辑,其作用就是明确地让计算机知道:在什么条件下,该去做什么.对于Python也是同样.Python之所以能完成自动化任务,比如自动抓取网页关键词,自动下载小电影等 ...

  3. 网易极客战记官网codecombat|当算法进入游戏,解开用代码画画的奥秘!

    什么是算法? 算法二字看起来玄而又玄,许多初入门的编程新手更是始终摸不清算法的门道,但其实,算法并没有你们想像的那么复杂玄奥. 百科对算法的解释为:算法(Algorithm)是指解题方案的准确而完整的 ...

  4. else if mybatis 嵌套_新手如何书写C++代码,远离深度嵌套的if-else

    有人喜欢if-else,称之为程序结构化的灵魂.确实,就计算机逻辑来讲,if-else最符合计算机非0即1的逻辑.就连在工作时,项目经理在给我们描述业务逻辑时也会来两句if如何,else如何!但是,也 ...

  5. 十年程序员的告诫:千万不要重写代码!

    对重写代码说不. 作者 | Roman Luzgin 译者 | 苏本如 责编 | 屠敏 出品 | CSDN(ID:CSDNNews) 以下为译文: 重写代码消耗了12个月! 我们从头开始重写代码浪费的 ...

  6. 前端代码规范(es6,eslint,vue)

    2019独角兽企业重金招聘Python工程师标准>>> 前端开发规范 一.HTML 1.语义化标签 HTML5 提供了很多语义化元素,更好地帮助描述内容.希望你能从这些丰富的标签库中 ...

  7. 在dw中如何调试html代码,如何在 Dreamweaver 中优化和调试代码 - Dreamweaver 用户指南...

    了解如何在 Dreamweaver 中清除代码.检查浏览器兼容性.验证 XML 文档并使页面符合 XHTML 规范. 清理代码 您可以自动删除空标签,合并嵌套 font 标签,以及通过其它方法改善杂乱 ...

  8. python代码块-Python 代码块

    代码块 骏马金龙https://www.cnblogs.com/f-ck-need-u/p/9925021.html 代码块可以使得一段python代码作为一个单元.一个整体执行. 几种代码块 模块文 ...

  9. HTML之表格篇——表格的嵌套

    表格的嵌套一方面是为使页面(贴子)的外观更为漂亮,利用表格嵌套来编辑出复杂而精美的效果,另一方面是出于布局需要,用一些嵌套方式的表格来做精确的编排,或者二者兼而有之.熟练地掌握表格的嵌套技巧并不是很困 ...

最新文章

  1. 对c语言这门课程的认识,一路走来,浅谈c语言的学习感想
  2. Python爬取B站5000条视频,揭秘为何千万人为它流泪
  3. JAVA---------华为机试------------提取不重复的整数
  4. python 示例_Python中带有示例的class关键字
  5. 经典面试题(21):以下代码将输出的结果是什么?
  6. 调用接口时form-data形式如何传递数据
  7. 运动目标跟踪(十三)--SRDCF/DeepSRDCF
  8. 2014年计算机求职总结--准备篇
  9. 小波变换与傅里叶变换的区别
  10. linux so替换,linux 热替换so文件
  11. GridView中使用DropDownList的OnSelectedIndexChanged事件
  12. 腾讯微博开放平台发布者分享会(陈思齐)
  13. form表单提交中文乱码的详细解析
  14. 当铺掌柜自制Typecho主题Pvcard主题
  15. PPT插入文本框的方法技巧
  16. 如何理解虚拟DOM?
  17. mysql命令创库创表_MySQL基础创库创表知识
  18. CSDN删除上传资源操作
  19. 201871010134-周英杰《面向对象程序设计(java)》第二周学习总结
  20. 普通人如何正确学习人工智能?

热门文章

  1. Loal-Blance Algorithm
  2. python实现计算移动平均值MA
  3. td中bug处理过程_TD bug状态说明
  4. TCP Illinois 与 TCP Highspeed
  5. 创业公司的管理工具集萃
  6. 搜房美国土地项目被指诈骗 市价为买家购价1/10
  7. Android应用开发实践-茶树害虫识别App
  8. 增强现实 Metaio扫描图片要求(Image Tracking)
  9. 零门槛开发AI陪练机器人,技能提高的神器。
  10. 单体项目部署多台机定时任务数据插入重复问题