所属文章系列及序号:寻找尘封的银弹:编码规范(一)
我见过很多编码规范,例如C++编码规范、Java编码规范、C++强制编码规范、C++建议编码规范。从这些规范的名字中,能看到一些“痕迹”:编码规范是分为很多种的,而分类方法也许不止一种。但是,到底有哪些种,有哪些分类方法?我倒是听到过有人谈论,但从来没见到能说得清楚的人,也没有看到过文档、文章、书里提到过。
有人会说:“把编码规范搞那么复杂,有什么意义,每个公司制定一份不就完了吗?”这个问题问得好!
我想请问:
“如果一个公司有多个部门,而部门之间很难协调怎么处理?”
“如果一个项目,到收尾阶段的时候,才想起了要改产品名怎么办?”
“如果在code review的时候,有严重分歧怎么办?”
“如果有人要求别人在C++语言中使用Java的规范怎么办?”
“如果编码规范内容有二义性怎么办?”
遇到这些问题时,我们可能会争得面红耳赤,也可能有人牺牲掉自己的观点,最后“和谐”共处。但是,这样无法提升公司的实力,更无法提升个人的能力!
这种“温水煮蛤蟆”的消磨实力的方式,我们经常用情商高来解释。但是时间久了,就发现只靠情商维系的关系,很可能是“谁做事谁决定”、“人亡政息”的结果,甚至会导致团队信任关系的隐性崩塌。而造成这个结果的原因在于:大家没有理清思路,不知道争论的层次是什么。
带着这些疑问,我们可以进入正题了。我认为编码规范是需要分类的,而分类方法有如下几种:
按照语言分类:分为通用规范、C++规范、Java规范、Objective-C规范等。
按照强制程度分类:分为强制规范和建议规范两类。
按照应用范围分类:分为业界规范、公司规范、部门规范、项目规范。
【语言相关规范】
语言相关的规范,本文就无需多说了,因为遍地都能找到相关的文档。不过,有一点需要注意一下:对于C++语言来说,类似《Effective C++》这类书中的有“技巧”的规范,是否也应该放到编码规范里?你能在后文中找到答案。
【通用规范:需要代码之外的能力】
对于各种语言的规范,都容易理解,而且这种规范在网络上俯拾皆是。而通用规范就需要解释一下了,它是建立在多种语言之上的,提取出这些语言共性的一种规范,举例来说,一个函数的代码行数不要超过100。
但是,制定编码规范的人提取共性、遵守编码规范的人记住并理解这些规范,并非那么容易,而是需要代码之外的能力。例如规范内容是“if条件不要使用细节,而要用意图”,但这到底是什么意思呢?规范文档中一般不解释,或者语焉不详。我在这里解释一下:
体现细节的代码:
if (((param1.flag & 0x0001) && (param2 != 1)) || !param3)
{...}
这种代码,我粗看肯定看不懂,看几分钟才能看懂,而且还会怀疑自己到底有没有看懂作者的意思。如果if后边的条件代码达到5行以上,那我就会怀疑这段代码会不会有逻辑漏洞,既怀疑自己又怀疑别人。
而体现意图的代码,就是那么简单明了,让人心旷神怡:
bool paramIsInvalid = ((param1.flag & 0x0001) && (param2 != 1)) || !param3;
if (paramIsInvalid)
{...}
那为什么要这样做?这是因为人脑思考问题是分层次的,一次只能理解到一个层次,跨越层次的代码,需要绕几个弯才能看明白。我的经验是:看这种绕弯的代码比较累,看懂一段代码之后,需要服用一次“镇静剂”,才有心情看下一段代码。
【强制规范】
强制规范,顾名思义,就是必须执行的规范,例如,必须使用四个空格来代替tab键。当然,这条规范,你也可以把它划到建议规范中,这完全取决于团队的约定。
【建议规范:充满创意和争议】
建议规范,就是可以执行也可以不执行。这就涉及到一些复杂问题了。强制规范都好说,大家意见一致,而且规范内容没有二义性,也不会发生遵守规范反而让代码更糟的情况。一到建议规范部分,情况就很复杂了,先说二义性,例如有如下规范条目:
函数代码行数应控制在50行以内,最大不超过100行。
Code review的时候,如果遇到的reviewer是一个态度很开放的人还好,一旦遇到一个比较认死理儿的角色,他一定要扣规范的细节,而不管多出一行代码是不是更加合理,有时甚至会把“建议规范”悄悄升级为“强制规范”,而大家还浑然不觉。
有争议的建议规范:
规范条目说,函数注释要完整,要做到面面俱到,内容包括作者、意图、输入参数、输出参数、实现方式、注意事项等。
做到这些并不难,不过它会导致代码过于冗余,更为严重的是“以辞害意”。也就是说,写代码的人主要关注于写注释了,而忘记了代码的简洁性,有时用一个点出关键点的函数名,远比一堆拖沓的注释重要!
正如Martin Fowler在《重构》一书中提到的:“在非必要的情况下不要写注释。当你觉得需要去写一段注释时,你首先应该尝试去重构代码,这将使任何注释都变得是多余的。”
【业界规范,公司规范,部门规范】
业界规范自不必说,像C++等特定语言的规范就属于业界规范。另外,业界规范也包含一些通用规范,这些已在前文提到,这里不必细说。需要注意的是:业界并没有一个统一、明确、大而全的通用规范。
对于公司规范和部门规范,如果公司人不多,那么部门规范就可以升级为公司规范。
【项目规范】
项目规范,看起来和部门规范可能会产生范围交集,不过重点不在这儿,而在于项目规范的目的是:拥有的统一词汇表。就像《领域驱动设计》一书里反复提及的通用语言(Ubiquitous Language)模式一样,如果没有这个词汇表,沟通成本和误解概率都会直线上升。
我曾经见过一套产品代码,遍地是缩写,而又没有在注释里说明缩写的意义,更没有文档去说这件事。好代码是可以成为一件“艺术品”的,这种代码会让程序员建立瞬间联想,从而快速理解代码,这是一种美感,也是很多欧美程序员乐此不疲的重要原因。而那些“不知所云”的缩写,会让程序员的灵感瞬间变成了枯燥的记忆。
我还见过一套产品代码,居然用公司CEO的名字缩写作为某个模块的名字,这让我看到了英国同事的那种个性化,那种隐藏在英国绅士风度背后的个性!
有一次,团队写好了80%的代码之后,却发现我们的产品名需要修改,还有模块名需要修改。当我们尝试去修改时,发现可能会导致上千条的编译错误、甚至逻辑错误时,我们放弃了。就这样,由于没有在开始的时候讨论清楚,也没有做成可扩展的代码,而那个“灵光乍现”的模块名,永远地刻在了产品代码里,与其他模块的不协调一直困扰着后来看代码的人们。
【结束语】
这三种分类方法之间,既有相关的情况,又有无关的情况,处于一种难以说清楚的状态。例如通用规范可以进一步分为强制规范和建议规范,而其规范内容在业界并没有统一的说法,一般都是从属于特定的语言规范中,需要自己去提取。
另外还有一个难题:通用规范是否应该包括《重构》《Effective C++》等书中的规则?
最终我们必须落实到制定几份规范,制定什么内容的规范?具体的例子和讲解,我会在后续文章中给出,敬请期待!
作于2018-4-12
网友评论