代码简洁之四 统一抽象层次
我是一个phper,但是也写java,ruby,python,go等代码。最近一直focus on coding in clean,觉得抛开语言的门第之见,从思想上总结如何写更优雅代码的方式和方法,也希望阅读了本文的朋友留言讨论。当然我还是用php做代码演示,不过灵感是来自于《代码简洁之道》的java代码。
首先我要提出一个概念:写代码和写文章是完全一样的事情。
文章可以写得短小精悍,也可写得冗余拖沓。可以写得言简意干、调理清晰,也可被写成云里雾里的天书。
同样的现象也会发生在我们写得代码上。一千个coder可能写出超过一千种style的代码。那些丑陋的代码不仅讳莫如深,而且会腐烂到让整个程序都崩坍。
写好一手好代码, 我觉得涉及的点太多,我下面只谈一个概念:封装的抽象层次。
其实代码无论是面向过程,还是面向对象方式或者AOP方式去编写,只是代码的组织方式不同罢了。具体到每一行的书写,我希望是做到一行一个调用,每个封装的函数是同一个抽象层次,每个函数里面的封装调用也是同一个抽象层次。如果不在同一个抽象层次,那么就会让可读性变差,让人不知道总的步骤逻辑的脉络 ,每当这个时候就需要move methods和extract method。
做一个更好的攻城狮,需要有好的鼻子,能够smell到坏代码中的bad 味道。
最近在写一个API项目,还是用的restful+mongo方式,然后在model层里面我对验证的钩子程序作了验证调用的封装,大致如下:
public function validation() { $this->checkRequire(); $this->checkPhone(); $this->checkNumeric(); $this->checkRegion(); return $this->validationHasFailed() != true; }
每一行调用都是检查某类型字段的有效性,看起来也没什么错。但实际上最后一行的checkRegion和checkPhone的抽象程度比其他几个要低,他们已经不是某类型的字段的检查,而是具体到phone字段的有效性和行政区域的有效性的检查。我们可以根据实际的业务做一些调整,我的调整如下:
public function validation() { $this->checkRequire(); $this->checkRegex(); $this->checkNumeric(); $this->checkArray(); return $this->validationHasFailed() != true; } /** * 检查所有涉及需要正则匹配有效性的字段 */ public function checkRegex() { $this->checkPhone(); $this->checkId(); $this->checkWeiXinID(); } /** * 检查所有至少必须是数组类型字段 */ public function checkArray() { $this->checkRegion(); $this->checkTags(); $this->checkServices(); }
这样改了之后,感觉代码层次更分明了,也更容易阅读。其实很可能有很多人会反对我的调整方法。他们会吐槽:把这些调用层次弄那么深入,感觉会加到代码运行成本,也让查阅代码需要不断地跳转调用的地方,还让代码变多了(因为有新的一层封装,当然代码行数会增加)。其实写代码就是在找平衡,我还是非常支持在《代码简洁之道》里面说的成本维度顺序:
代码简洁性。
功能完整。
执行效率(速度)。
编程所用时间。
健壮性。
灵活性
编程本就是折中的结果。时间成本和质量之间做平衡。
放在第一条的就是简洁,而简洁最重要的一点是可读性。调用层次深在语言层次上的消耗可以忽略不计,随着php7的稳定版发布,它的性能已经double了,何况还有zend做一层代码缓存的方法。再者查询代码可以使用高效的ide(或者你像我一样是vimer死忠,早就把vim弄得插件化了)去查阅,完全不会增加阅读难度,反而由于抽象层次一致,更理解代码上的实现和业务逻辑的梳理。