扫码阅读
手机扫码阅读

聊聊代码的整洁(上)

98 2024-01-31

这是鼎叔的第七十篇原创文章。行业大牛和刚毕业的小白,都可以进来聊聊。

《代码整洁之道》这本书最有影响力的一个观点,就是代码的好名字本身就解释了最重要的信息,如无必要,不要增加代码注释。这个观点和传统软件质量观点是不同的。

Part1 为什么代码需要整洁‍‍‍‍‍

代码永远都需要有人来书写,因为我们永远无法抛弃背后的精确性。人工智能无法创造出满足客户模糊感觉的成功系统来,它无法透彻地理解需求,需求的细节是无法被忽略或抽象的。‍‍‍‍‍‍

经常有人说烂代码回头有空再清理,实际上再也没有机会清理,这就是LeBlanc法则-稍后等于永不

能分辨代码是否整洁,并不代表能写出整洁代码,实践上需要贯彻大量的小技巧,刻苦才能习得“整洁感”,好的“代码感”能帮助程序员选出最好的方案。‍‍‍

程序开发大师们对于整洁代码的定义如下:

优雅和高效的代码令人愉悦,整洁代码只做好一件事。每个例程都像是为了专门解决某个问题而存在的。

整洁的代码在细节上花心思,果断决绝,展示出要解决问题的张力和表达力。阅读代码时无需花费力气,简单到一气呵成。‍‍‍‍‍

整洁的代码便于让其他人增补,可读性强,代码块越小越好,没有重复,充分体现了系统的设计理念,且只包含尽量少的实体。

代码的整洁还和测试息息相关,光有可读性也做不到“干净”。

整洁代码的实践军规,就是每次代码签入时,都比签出时干净‍‍‍‍‍‍‍

Part2 有意义的命名

给代码起好名字的几条简单规则:‍‍‍

一 名副其实

多花时间选择好名字,一旦有更好的命名,就替换旧的。变量、函数和类的名称应该已经答复了所有的大问题,包括为什么存在,做什么事,该怎么用。如果你还需要注释来补充,那就不算是名副其实。‍‍‍‍‍‍‍‍

比如,表示消逝时间的名称d,可以换成XXXTimeInDays。再如,开发扫雷游戏,盘面是名为theList的单元格列表,可以改为gameBoard。‍‍‍

如果我们发现代码用状态值4来表示“已标记”,就用isFlagged来掩盖4这个魔术数,提高可读性。

二 避免误导

开发者应该避免留下掩盖代码本意的错误线索。比如:

不要使用平台专有名称,比如Unix平台的“hp”,“aix”。名称中谨慎包含“list”,它对程序员有特殊含义。

提防使用外形相似度较高的名称。提防拼写前后不一致带来的误导。如果相似的名称按字母顺序放在一起时,差异很明显,那就相当有助益。‍‍‍

警惕命名中包含的小写“l”和大写“O”,它们看起来像数字1和0。‍‍‍‍‍‍‍‍

三 做有意义的区分

很多人喜欢按数字系列命名,如a1,a2,a3...aN,这样的名字非常误导,没有提供导向作者意图的线索。如果我们想把a1的值赋给a2,我们可以把参数名称改为source和destination。

废话是另外一种没有意义的区别,比如ProductInfo和ProductData这两个类,从含义上看没有不同。‍‍‍

废话都是冗余。比如在变量名中出现variable,在表名中出现table。NameString中的String都是废话,名称肯定是字符类型。‍‍‍‍‍‍

如果缺乏明确约定,很多变量看起来就没有区别。‍‍‍‍‍‍‍‍‍‍‍

四 使用读得出来的名称

人类对于能读出来的单词就更容易记忆,不善加利用就是一种耻辱了。另外,读不出来的名称,在开发者讨论时就会显得特别傻。‍‍‍‍

例如,genymdhms这个函数,本意是生成的日期-年月日时分秒,我们修改为生成时间戳-gengeration timestamp,就舒服多了。‍‍‍‍‍‍‍‍‍‍‍‍‍

五 使用便于搜索的名称

开发环境经常会通过代码搜索来提高效率,如果使用单字母名称(比如e)或纯数字常量(比如7),在搜索时就会很郁闷。‍‍‍‍‍‍‍

名称长短应该和它作用域大小相对应,尤其对于可能多次使用的变量或常量,要赋予其便于搜索的名称。比如WORK_DAYS_PER_WEEK比数字5要好找得多,也体现了作者意图。‍‍‍‍‍

六 避免使用没有共识的编码语言‍‍‍

早期的编译器不做强制类型检查,程序员需要用匈牙利语标记法帮助自己记住类型,但现代编程语言不需要了,也降低了类型编码误导读者的可能性。‍‍‍

人们也习惯无视代码的前缀(如m_)和后缀,只看道名称中有意义的部分。也不需要使用“I”前缀来代表这是个接口,只需要告诉对方我们要实现什么。某些情况下,我们可以用前缀给一系列相关变量提供必要的语境,让读者明白它们是某个更大结构的一部分。‍‍‍‍‍‍‍‍‍

记住,只有程序员才会认真读你的代码,原则上多用计算机科学的术语,少用涉及业务领域的专有名词,毕竟协作程序员不可能经常跑去问客户这些名词的含义。但是如果没有更贴切的术语来命名,用业务领域的专有名词也是可以的。‍‍‍‍‍‍‍‍

七 避免抖机灵

专业程序员把“明确”作为王道,编写他人善于理解的代码,而不是炫耀聪明,也不是用笑话耍宝来展示幽默感。

也别用“双关语”,将同一词语用于不同目的。比如别用eatMyShorts()来表示abort()。‍‍‍‍‍‍‍‍‍‍

八 精确,每个概念对应一个词‍‍‍

给每一个抽象概念选定一个词,并一以贯之。

类名和对象名应该是名词或名词短语。名字的精确性很重要,比如address类下的web地址实体,我们可以使用PostalAddress,MAC,URI来命名。主要短名词足够清楚,就比长名称好。‍‍‍‍

方法名应该是动词或动词短语。‍‍‍‍‍‍‍‍‍

起好名字最有挑战的地方是,需要良好的描述技巧和共有的文化背景,需要教学,持续下去,效果会立竿见影。‍‍‍‍‍‍

Part3 函数的整洁

足够短小

函数的第一条规则是短小,第二条规则是要更短小。函数应该只做一件事情,做好这件事情‍‍‍‍‍

上个世纪,对于函数短小的说法是,函数长度不超过一屏,相当于20行,到今天,我们也认为20行以内的函数长度最佳。‍‍‍‍‍

像if,else,while等语句,其中的代码块最好就是一行-比如就是一个命名清晰的调用函数,即函数不应该大到足以容纳嵌套结构。‍‍

只做一件事的函数是无法被合理地切分为多个区段的,函数中的语句应该在同一抽象层级上,否则,读者无法判断某个表达式是基础概念还是细节。

我们想让代码拥有自上而下的阅读顺序,每个函数后面都跟着位于下一个抽象层级的函数,并说明为了达到什么目的而调用它。

函数的参数

函数的参数个数,最好是0,其次是1,再次是2,尽量避免3 。参数们带有太多的概念性,它们和函数名不在同一个抽象层次,需要读者了解目前并非特别重要的细节。如果参数较多,说明其中一些参数应该封装成类。

从测试角度,参数越多,确保参数各种组合的运行正确性非常困难。输入参数如果是布尔值,更违背了本函数只做一件事的初衷。

使用输出参数更让人困惑,要小心其是否导致古怪的时序耦合错误。我们习惯于信息通过返回值从函数中输出,而不是通过输出参数。

对于单参数函数来说,参数和函数应该形成非常良好的动名词对的形式,如writeField(name), assertExpectsEqualsActual(expected,actual)。

函数要么做什么事,要么返回什么信息,不要两者同时干,这样容易混淆。

错误代码处理

发生错误时,应使用异常替代错误码,这样就能把异常处理代码从主路径代码中分离出来,达到“每个函数只做一件事”的目的,处理错误的函数不应该做其他事。而返回错误码,就是在要求调用者立刻处理错误。

使用异常替代错误码还有一个好处,新异常就可以从异常类派生出来,无需重复编译或部署。

别重复自己

重复可能是软件中一切邪恶的根源。整个模块的可读性会因为重复的消除而提升,许多实践规则都是为了控制重复而创立的。比如,面向对象编程中,将代码集中在基类。

结构化编程

所谓结构化编程,就是每个函数,以及函数中的每个代码块,都应该只有一个入口和一个出口。那么,老编程语言中的GOTO语句就不应该存在,而循环中也不应该有break语句。越是在大函数中,这些规则就越有效。

写代码和写文章类似,一开始没有严格规则,不断推敲打磨,直到它变成心目中的样子。大师级程序员都是把系统当成故事来讲,而不是当作程序来写,函数编写必须被干净利落地拼装在一起,形成精确而清晰的语言,帮助我们讲好故事。

对象和数据结构

使用数据结果的过程式代码,难以添加新数据结构,因为必须修改所有函数;面向对象代码难以添加新函数,因为必须修改所有类。

Demeter定律认为,模块不应该了解它所操作对象的内部情景,对象应该隐藏数据和内部结构,暴露操作接口,因此,方法不应该调用由任何函数返回的对象的方法。

最为精炼的数据结构,是一个只有公共变量,没有函数的类,它有时被称为数据传送对象(DTO:Data Transfer Objects),主要被用于数据库通信和解析套接字消息之类的场景中。

错误处理

当我们提到错误处理,首先不要返回null值,也不要给其他方法传递null值(除非API指定这么做)。

下篇,将分享注释、代码格式、类、系统如何做到整洁,以及代码坏味道的处理清单。

原文链接: http://mp.weixin.qq.com/s?__biz=MzkzMzI3NDYzNw==&mid=2247484314&idx=1&sn=0f05296ca6ab3efe6b236c0a776230e6&chksm=c24fb6f8f5383feecdbe837e5210c57e73770c953817d7979fda0804db9e9b4d94847e1209b2#rd

《无测试组织-测试团队的敏捷转型》主题探讨。从打造测试的组织敏捷,到敏捷测试技术的丰富实践,从一线团队的视角来聊聊我们是怎么做的。面向未来,拥抱敏捷原则,走向高效能组织。

81 篇文章
浏览 15K
加入社区微信群
与行业大咖零距离交流学习
软件研发质量管理体系建设 白皮书上线