你会给变量起的什么名字

在进行项目编写的过程中,免不了的就是要进行各种方法、变量的命名。你一定经历过对变量命名无比困难的时候。而有的时候命名就看起来很轻松,比如新增一个领域实体的时候。那用户(user)来举例子,可能还没有进行更多的思考,但 addUser的方法就已经创建了。工作完成,可喜可贺。

但真的是这样吗?有多少同学在进行后续的代码翻阅的时候,反而发现别的同学的接口使用了saveXxx的命名方式?该跟他保持一致吗?还是继续我行我素?

如果在团队中的命名规范不明确的话,这种情况确实会出现。而比表面上看起来更严重的问题是:save和add真的一样吗?如果在没有统一的命名规范的情况下,save 和 add中可能存在语言的区别,当别人看见你的接口命名发现不一样,而你的注释又不那么完备的时候。如果使用你接口的同学尽职尽责,那他可能会花费时间去确认你底层业务逻辑是否一致。又或者这个问题先放一放,让问题到自测的时候再去检验。

这个问题不好直接回答。但尽管不能精准地描述出一个概念,我们却可以用一系列描述勾勒出命名的轮廓:

那么每当你对一个对象起名的时候,可以快速地在脑内回忆一遍列表,看看他是不是满足以上的内容。一开始可能需要对照着Check List,但当这个步骤熟练了之后,就可以一闪而过判断出:“目前的不够好,让我再想想”。

仅有Check List并不完全够,我们还需要理解一下概念。能够快速反映出来场景,用反例的方式来判断条件是否满足。

意如其文

我觉得应该是最好理解的一条了,好的代码应该是有解释性的,命名可以表示出你要进行的“动作”、你可能需要的内容、以及你希望修改的东西。取名叫 endTime就好过 t。同样的,在for循环中的i是什么,需要在看for中所有的逻辑的时候都记着,那索性就不如直接命名为 robotMoveStep,这样还可以释放本身就宝贵的脑细胞。而对于一些魔法值例如 4如果不知道上下文的话,那就很难判断他所指代的是什么,这时候就不如用常量来替代,即便知识变成了类变量,也好多一个单独的 4在代码里。

避免歧义

在阿里巴巴的代码规范中有几个例子就如此例如:对于Long类型 用大写的字母表示比如 1L,就是为了避免 11和 1l(1L的小写)的尴尬。同样的,如果使用了一些特殊类型的命名时也会这样,比如 userList,结果发现其实是一个它是一个 int[],或许它的背景是从一开始的 userList变成了 userIds,但无论如何代码总是胜过于解释。

无冗余的信息

一般来说,我们不会主动给命名增加冗余信息。user就是user,但是如果在当时想要新增一个新的子领域对象的时候扩展呢?userInfo、extendUserInfo、extendUserInfoNew?这并不是不会发生的事情,事实上正因为他发生了,且一旦发生就难以调整,我们才要极力地避免。添加一个后缀看起来是一个简单区分的好主意,但是这个实际上也和“意如其文”是一个问题。当一个新的人仅仅看到名称了之后并无法区分他们有什么区别,那么可以进行开发就需要对他们了如指掌才行。仅仅为了确保修改一个功能正确,只能一个一个去确认逻辑,把上下文翻个底朝天。冗余的是废话,是无意义的,就应该去掉,如果真的需要添加区分,就应该增加可以提供鉴别能力的方式,例如 userContactInformation。

可以检索

你可以想想,当出现了一个bug,然后其他端的同学提示你说是由于传输数值的问题,而变量名是 sum。那么我想,下一步个流程就是确认接口是什么,然后一头扎进了 sum是怎么获得的道路上,有可能在过了很长时间之后,你才意识到这个 sum甚至不是你的代码里处理得到的,而是直接通过下游系统获得的。确实没有什么好办法,因为 sum充斥在工程的各个地方。如果这个名称不是 sum,而是叫 totalUserSubmitCount呢?我想在IDEA的帮助下,下一步应该是全局检索,而这么精确的名称涉及到的问题就也没有那么多了。当然,并不是说不能使用sum、count这种名称,但如果你使用了,就应该尽量让他们作为一个局部变量呆在一个方法里面。

无类型编码

java是一个强类型语言,你不需要特别地指定它的类型,所以 mobileStr这种的命名其实是冗余的,String mobile;就足够了,而当有一天 mobileStr变成了 Long mobileStr的时候,也不会引起额外的误会。

区分方法还是类名

java是面向对象的语言,所以一个类应该是描述的一个对象,那么我们描述一个对象的时候,应该是一个名词;而对象中的方法的时候则应该是对象所具有的能力,所以描述方法应该是动词。

统一命名

就如同开头说的,经常而言,对于save和add,他们的常用语义是不同的,如果混用会让吃过一次亏的人在看你所有的代码的时候都会小心翼翼。那么 get和 find又有什么区别呢?如果这个概念没有统一,那么你就需要记住隔壁小王是用的 find;隔壁小张的 save其实不会更新已有对象;对门小李的 add方法其实是追加(append)功能。

而除了开发同学之外,这个命名也最好和领域本身的命名是统一的。这样在即便是在跟对应的产品,或者业务方进行沟通的时候,在脑子里就不用进行思维上的转换,可以大大地提高沟通效率。举例而言,如果存在两种领域的商家员工和个人用户,那么直接使用 customer 和 employee大家都可以顺畅理解,而如果要叫 user 和 userV2的话恐怕就要再约一次会了。

但是如果这方面已经不涉及领域信息了,那也尽量和业内中的名词进行统一,比如redis的 aof,或者是mysql中的 binlog,这种已经存在且有一定语境的名称,可以提高他人理解你业务的速度。

特别注意的是这个“ 命名 ”对于它的中文描述名称是一样的。

为命名创造合适的语境

因为名称之间本身就是有解释性的,所以在进行一组一组命名的时候,就可以提供上下文信息。例如如果有一组方法 login()、logoff()那看起来就是用来进行登录登出的;又或者是一组属性如:price、payTime、couponCode,那么看起来就是订单相关的对象。想这样将一组有交互关系的名称放在一起的时候就可以为支撑的对象、业务提供一个语境,从而帮助理解业务流程。

但是要尽量避免添加无意义的语境,比如 orderPrice、orderPayTime、orderCouponCode,那么他们中的order显然是无意义的,因为并不会对属性的命名提供帮助,而试试上在实际的值的获取时我们一般是使用 order.getOrderPrice(),那这时候的order就显得更加冗余了。

本文在《Clean Code》中列举的很多例子的基础上,加入了自己的个人理解。当然也忽略部分书中提到的点,觉得不是问题或问题不太严重:比如“尽量使用可以读得出来的命名”,而在在沟通的时候可以使用中文;又比如“别扮可爱”,在目前比较卷的环境中大家相对都很专业,出现概率比较小。

当然每个团队都有自己的一套规范,总的来说可以让coding更加顺利,我们的目的就达到了。