大家好,这里记录,我每周读到的技术书籍、专栏、文章以及遇到的工作上的技术经历的思考,不见得都对,但开始思考总是好的。
编程礼节
这个词,我武断地下个结论,估计你很少听到。
在上周的技术思考那篇文章中,我们提到程序员之间最经常使用的沟通媒介是代码,程序员之间的互相尊重也体现在代码里面。
代码也是一种给人看的文档,而文档的本质在于交流。
在编程中,良好的交流意味着读代码的人能够理解、修改和使用代码。
另外,我们也提到,Bob大叔在《代码整洁之道》这本书中,曾说过,“人们说脏话的频率是衡量代码质量的唯一标准”。
那么肯定没有比吐脏字,表现的更不尊重了吧。
到底发生了什么,能让你到了[骂人]的地步了呢。
我们一起想想看,是不是,无论什么形式的沟通,最基本和最初级的尊重表现形式,肯定莫过于礼节了,对吧,如果你的代码呈现出的“编程礼节”总是带有“微笑”的表情,是不是就能起到程序员之间交流时候的润滑剂效果呢。
那怎么样才能让我们的代码看上去是“笑脸相迎”呢,我们说的编程里面的这种“礼节”,具体的表现形式又是怎样的呢。
礼节一:编写“养眼”的代码
我说,最“表面层次”的礼节,就是,把我们的代码“打扮”的干净、利索、整洁。
图1
让我们一起来看看下面三段代码。
这三段代码的内容都是一样的,这里的“一样”,是说,我们的PerFormanceTester这个类需要3个不同的TcpConnectionSimulator实例,而这三段代码都完成了这个任务。
这三段代码又是不一样的,这里的“不一样”,是说,你所看见的,如它们的换行、排版、注释多少都是不一样的。
代码段1
代码段2
代码段3
好了,当读到这里,你应该把上面三段代码都看完了,你觉得哪段代码更让人悦目呢。
你肯定不会选择代码段1的,那个排版首先就让人不舒服,你可能会说,代码段2看上去已经很可以了,格式整齐,是的,在整洁性上,它比代码段1肯定强了不少,你再看看代码段3呢,它又把代码段2中的重复性注释,都规整到一行里面了,这样是不是“看上去更养眼”很多呢。
要想让源代码“看上去养眼”,这里有三条技巧性原则送给大家。
使用一致性的布局,让读你代码的程序员很快就习惯这种风格。
让相似的代码看上去相似。
把相关代码行分组,形成代码块。
一段具有良好可读性的代码,它的首要条件,也是要具备整洁性第一,或者索性我们就管它叫做“整洁性第一”原则。我个人觉得这个叫法,也未尝不可的。
如果,你遵循上面我们说的那三个“养眼”原则。你选择好的变量命名,写好的注释,把代码整洁地写成更好的格式。这样,当别人读你代码的时候,会感到很轻松,也能很快速地理解你所写代码的含义,而且,这个“别人”也很有可能是几个月之后的你。
礼节二:编写“浅栈”的代码
这里的“栈”,指的是“思维栈”,“浅栈”也就是不需要怎么思考,就能很轻易地理解。反应到一段代码上面,也就是可读性强。
一团乱糟糟的代码,肯定会阻碍你进行阅读,就像上面那副漫画所展现的那样,你去哪里找你想要的档案呢。更不用提,读完代码,还需要继续在原来代码的基础上做修改和添加新的功能了。找代码都找不到的情况下,又如何添加新功能,你想想,是不是。
可读性基本原则:
代码的写法应当使别人理解它所需要的时间最小化。
让我们来看两段代码,你认为哪种方式更容易阅读和理解呢?
if(length >= 10)
还是
if(10 <= length)
这一段又是哪种写法更容易阅读呢?
while (bytes_recevied < bytes_expected)
还是
while(bytes_expected > bytes_expected)
都是第一种方式,对么,为什么都是一种方式那样的写法?
在《编写可读代码的艺术》一书中的第七章有一条指导性的原则:
你或许觉得这样有点牵强,可根据我们看到的这个原则,它也是符合一定心理学基础原理的,不是么。
那好,我们看下面这段代码,估计你的感觉就会更近些了。
if(a == b){ //Case one ...} else{ //Case two ...}
还是
if(a != b){ //Case tow ...} else{ //Case one ...}
更容易阅读和理解呢。
还是第一种方式,对不对,这又是什么原理呢,还真有个原理:
首先处理正逻辑而不是负逻辑的情况。
我们上面所举的代码的例子虽然都很简单,但它足以说明问题,你会发现第一种方式都比第二种方式更简单,这也是阅读起来更容易理解的保证。不要把代码写复杂,有时候一些年老的,或者号称经验丰富的程序员,故意把代码写的相对“复杂”,不然似乎体现不出他的专业性。
比如,拿我们常用的三目运算符来举例。
if(hour >= 12){ time_str += "pm";} else{ time_str += "am";}
这样是不是很好理解,可是“专业”程序员,会这样写。
time_str += (hour >= 12) ?"pm" :"am";
你说,也容易阅读呀,那如果碰到下面这样的呢。
我们首要目的,是要把代码写的简单。
相对于追求最小化代码行数,一个更好的度量方法是最小化人们理解它所需的时间。
Bug喜欢出现在复杂的地方。
如果软件代码出现线上问题,经验告诉我们这些问题常常会集中在某一个区域,这些区域都有一个共同的特点,那就是复杂。
编写代码时如果追求简单易懂,代码就很难出现问题。
到这里相信你已经对什么是可读性的代码有来自己一定的理解。
接下来,为了让大家的可读性理解印象更深刻,我们再”加点料“,我为大家准备来下面这两段关于if嵌套代码例子。
代码段4
代码段5
关于可读性这块,最后问你,代码段4和代码段5,哪个更容易阅读理解呢。
答案当然是代码段5,当你读代码段4的时候,很是考验你的“心智消化系统”,每个嵌套层次都会在你的“思维栈”上又增加一个条件。
通过提早返回,来减少嵌套。
礼节三:编写“有型”的代码
优质代码都有型,开发优质的软件需要基本技法作为支撑。
我一周去健身房的次数基本在4次以上,起初我锻炼的目的是为了让自己看上去不是那么瘦弱,强壮些更好。但随着我对健身的深入理解,后来我的目标就变成了,让自己的体态看上去匀称,也就是有点“型”,那如果让自己的体态变好,我一般就是练三个部位,胸、背和腿,这些是撑起一个人良好体态的基础“模块”
为了练好身体的这些模块,我会选择比如使用杠铃卧推、坐姿推胸、器械飞鸟等动作练胸,使用高位下拉、坐姿划船、俯身划船等动作练背,使用杠铃深蹲、箭步走、杠铃硬拉等动作练腿。
那代码如果要有”型“应该锻炼哪些“意识部位”呢,我个人认为在面向对象编程领域里,有基本的三个“部位”是一定要锻炼的,那就是封装、继承和多态。
为了练好面向对象编程的这些模块,我们有什么动作呢。根据《编程的原则》一书中给出的建议,我们有以下10个动作。
抽象
封装
信息隐藏
打包
关注点分离
充足性、完整性、原始性
策略和实现的分离
接口与实现的分离
单一引用点
分治
正如我健身的时候,为了有效锻炼,我肯定要选择更有效的动作,来达到体型的锻炼,面向对象编程我们也会有被人们使广泛的动作,我从中选择来抽象、封装、关注点分离来跟大家做一个沟通。
因为大家已经有相当多的面向对象编程的实践经历了,我这次并不打算在这篇文章中像上面那样列举一些代码段,我计划也“抽象”一次,给你一次“三行而后思”的机会。
抽象
抽象的反面是具体,我们日常中接触到的事物基本都是具体的,如果面向具体编程,把一个一个的具体事物通过编程的方式实现出来,这样将会耗费我们无穷的精力和时间。为了提高我们解决问题的思考效率,就需要我们在各个具体的事物之间进行不断总结,从具体的对象中抽出共同的性质,再将其固定为更加通用的概念,设计模式也是这么来的。
抽象是人们在处理复杂事物时使用的基本手段,在思考效率上,抽象远胜于具体。
封装
程序的组成包括数据和逻辑,我们把相互关联的数据和逻辑分到一组,再定义为一个模块,这就是封装。这样一来,一个一个的模块都是独立的“零件”,我们就有机会去重复使用它们。同时呢,当我们将来有需求过来,要涉及到修改这些模块中的代码的时候,这些修改带来的影响也都会被限制在这些模块内部。
有了关联性很强的数据组成的数据结构,以及关联性很强的逻辑组成的函数群之后,我们便能获得更简洁且高质量的模块了。
分离
这里说的分离就是关注点分离。所谓关注点,也就是我们程序要实现的功能和目的。我们把各个关注点相关联的代码集中在一起放入独立的模块,这种方式就是我们践行关注点分离的动作。
那这里的模块,不就是上面所说的封装的产物么,其实,大家只要意识到这一点也就说明对封装有了更进一步深刻的理解。只谈封装,其实只是做到了模块化的第一步,当谈到关注点分离的时候,就是我们模块化的第二步。
关注点分离中最具代表性的模式,莫过于是“模型视图控制器”(ModelViewController,MVC)。以关注点为单位创建的模块,会要求我们把不同的和不相关的代码以及功能分开,比如这个MVC的模式。
如果一个模块在不同前提下有不同的功能,我们就需要分割模块,让各个模块处于独立的状态。
恭喜你,又完成一次思考。
参考资料:
1、图1、代码段1、代码段2、代码段3、代码段4和代码段5引自《编写可读代码的艺术》;
2、漫画人像图来自网络;
忆往期: