掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

为什么我们辛苦写出的代码却难以阅读?

词汇和命名

词汇是思考的材料,如果我们的词汇贫乏,我们的思考也必然破碎。优秀的作家和普通人的差别,很大程度是在词汇的丰富程度上面。据说丘吉尔能掌握四万个英语词汇,而一般中国人,能有几千个词汇量已经很少见了。因此我们在用英语来写代码的时候,常常会陷入词汇不够的困难境地。

我们在程序代码中,往往看到很多类似icount、var、num这样名字的变量;还有很多叫做manager、controllor的类,这些都是因为我们想不到应该如何命名导致的。除了名词缺乏,我们的动词往往也很缺乏,证明就是:我们的很多函数名都叫Process()、Run()、Poll()、Loop()诸如此类。如果我们把程序源代码看成是一篇文章,那么这篇文章的词汇,就是各种变量和函数的名字。如果我们在命名上困难重重,这篇文章也一定晦涩难懂。

命名上的困难,除了是因为我们英语词汇量太小以外,另外一个原因,是我们对于业务领域的不了解。我们在接到需求后,往往就急着开始所谓设计和开发:我们会说在这里要用一个哈希表来存放数据,那里用一个回调函数来处理异步结果。

如果我们把程序看成仅仅是一些数据和算法组合,那么我们的命名必然也只是局限于这些数据结构、算法的概念上,比如我们常见到xxMap的结构体,还有xxCallback的函数指针。用这样一堆名字构建起来的程序,就好像摩斯电码一样难以理解。尽管在这些看起来都差不多的字符背后,实现的是一个鲜活而独特的业务需求,但是光看字面是完全无法想象出来的。这个问题实际上也很好解决,就是我们在写程序之前,多去了解这个程序所在的应用领域,看看这个应用领域里面到底是有些什么样的词汇。

比如一个商业应用中,就会有Bill、Invoice、Deal等等专用词汇,在游戏应用中,有Player 、NPC、Monster这些概念……我们可以在几乎任何时候,都能从这些业务领域中攫取大量的词汇,来替换掉计算机领域中少的可怜的几个词。如果我们真正的把代码中的命名,变成应用领域的词汇,那么这样的代码片段,就是一个描述某种业务领域的文章,如此,可读性就能大大的加强。

命名本身并不影响程序的运行,但是我们也没必要直接写出好像被扰码器处理过一样的代码。如果我们的命名词汇既准确也生动,那么我们的代码一定也是非常容易读懂的。特别是,我们阅读代码的目的常常不是要评估代码的算法,而仅仅是找到某段业务逻辑的位置来进行修改,这样一个和业务逻辑有关联的命名,能让我们快速跳过大量不相干的代码,直接定位到需要修改的地方,这对代码维护是非常有利的。

语法和模型

传说上帝为了惩罚傲慢自大的人类,让人类开始说不同的语言,而因为说着不同语言,人们无法合作,就无法合力建造巴别通天塔去挑战上帝了。这个故事说明了,语言的不同,能造成多么大的沟通障碍。我以自己少的可怜的语言知识,都能发现不同语言之间,一些重要的区别,是如何阻碍人类之间的沟通的。

第一个是语序问题:汉语和英语,语序都是主谓宾结构的,但是日语和韩语,语序则是主宾谓的,当我们说“我爱你”的时候,在日语韩语的语法是“我你爱”。这种语序上的差异,会导致整个表达方式的不同,从而造成严重的沟通障碍。这样看起来好像汉语和英语还挺接近,但是在定语状语的语序上,这两种语言又是不一样的,汉语的定语在修饰对象的前面,而英语即可在前,也常常在后,比如Man in black。

第二个是语言态度的结合问题。我们常常听到韩语有“思密达”的结尾,实际上这个词汇没有实际含义,只是表示尊敬的含义。在日文中也有大量的这种词汇。这是所谓粘连语的特征,但是在汉语中,我们则使用不同的敬语词汇来表达,比如我们称你为“您”,而英语则通过不同的虚词用法如would来表达敬语语气。我们不得不说人类语言真的是非常复杂。

各种自然语言的不同之处除了上面所说的,还有一个重要的差异,是在于词汇的分布不同。英语中的名词比汉语要多的多,因此才会出现所谓中国的“羊”年,在英语中都不知道应该如何翻译的情况,因为英语中绵羊、山羊都是不同的词。而汉语中的动词,则比英语的多,英语有大量的动词含义是通过动词短语来表达的,比如give in/give up,汉语就是完全不同的“让步”和“放弃”。

这个差别体现在编程上是非常关键的,我们知道,面向对象编程,需要以对象类型来对业务代码建模,而由于汉语名词的缺乏,我们常常表达一个对象时,找不到一个专有名词,而是用“做什么什么的东西”来表达这个对象,这对我们代码的设计造成极大的困扰。因为行为的特征在对象上往往是不够稳定的,一旦我们以行为作对象的名字,而这个对象在后续的迭代中被修改行为,就很容易出现名不副实的情况。

因此我们在设计面向对象代码的时候,还真的不能仅仅以汉语的习惯去设计,而是要多找找有没有专门表达这个对象的英语名词。

重复和耦合

我们如果想写出如同自然语言一样易读的软件代码,那么我们就一定要以自然语言写文章的结构。但是很可惜的是,自然语言的文章以传情达意为目的,而软件代码主要是控制电脑工作的任务列表。这两者之间有一个重要的差异,就在于“语句”的存在形式上。

我们知道,代码是一行行执行的,而电脑对于数据的处理,往往存在很多类似的、重复的处理步骤。而我们写一篇文章,肯定不会让某一个描述过的句子,反反复复的出现在所有需要的地方。我们会用一些概念词汇来代表这些重复提到的概念,我们会有很多专属名词和缩写,比如“婚戒”“爆炒”这类稍微复杂一些的词汇。这种词汇和说法,能让文章变得简单清晰,突出重点。

其实我们的源代码也可以做到这点,基本的做法就是“封装”:我们把类似的、重复的代码封装成子函数;我们用继承的方法来构建相似的数据对象。如果我们还能用恰如其分的名字来命名这些子函数和子类型,那么我们的代码就能避免长篇累牍的重复代码,从而能让我们更容易的理解。也许,这种封装是一种“额外”的劳动,因为CPU可不管这些,如果你只是想算出结果来,那么完全可以一个函数从头写到尾。

但是如果你有意识的做一些有具体业务含义的封装,你会得到另外一个好处,就是代码能更方便的重用。代码重用的首要条件是代码可理解,封装正是对复杂的实现过程的屏蔽,从而让人可以快速理解。而业务领域的重复逻辑是非常常见的,如果代码刚好是一些典型的业务流程,那么这些对应流程的代码,就一定能被重用到大量的类似业务流程的处理那里,这样的好处不言而喻。

总结

这篇文章并没有很深入的去描述,如何从技术角度编写出可读的代码,而主要是关注软件代码和自然语言的差异和联系。因为自然语言本身是我们理解世界的基本工具,所以我们的软件代码,也应该要针对自然语言的特点去设计,才能满足我们人类对代码的理解需求。

聊聊架构.png

原文来自:聊聊架构

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

  • 个人/企业涉诉查询

    通过企业关键词查询企业涉松详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。

    通过企业关键词查询企业涉松详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。

  • 账号黑产风险识别

    根据手机号来查询是否命中黑产风险

    根据手机号来查询是否命中黑产风险

  • IP反查域名

    IP反查域名是通过IP查询相关联的域名信息的功能,它提供IP地址历史上绑定过的域名信息。

    IP反查域名是通过IP查询相关联的域名信息的功能,它提供IP地址历史上绑定过的域名信息。

  • 人脸卫士

    结合权威身份认证的精准人脸风险查询服务,提升人脸应用及身份认证生态的安全性。人脸风险情报库,覆盖范围广、准确性高,数据权威可靠。

    结合权威身份认证的精准人脸风险查询服务,提升人脸应用及身份认证生态的安全性。人脸风险情报库,覆盖范围广、准确性高,数据权威可靠。

  • 全国城市空气质量

    全国城市和站点空气质量查询,污染物浓度及空气质量分指数、空气质量指数、首要污染物及空气质量级别、健康指引及建议采取的措施等。

    全国城市和站点空气质量查询,污染物浓度及空气质量分指数、空气质量指数、首要污染物及空气质量级别、健康指引及建议采取的措施等。

0512-88869195
数 据 驱 动 未 来
Data Drives The Future