法则01:准确命名
如果你准备花一小时写代码,那么其中半小时得用于命名。这句话虽然有些夸张,但体现出命名的重要性。的确,类名、函数名、变量名、文件名等如果含混晦涩,会给维护工作造成麻烦。
命名时把握一个原则:对于函数说明其在“做什么”,对于类或变量说明其“是什么”。言简意赅是一种理想状态。首先要用词准确,特别是英文基础较差的人,请随时查查词典。然后把这些词拼成一个短语或短句。最后,如果名字太长,再做一些简化。
● 用词准确
英文好的人在准确用词方面会比较轻松,但英文不好的人,一定要多查查词典。甚至可以学学白居易写诗的方法,问问旁边的同事能不能读懂你起的名字。
我曾经在一个系统中需要管理若干“检查任务”,在给这些“检查任务”的基类命名时花了很长时间。“任务”一词在翻译软件里找到了4个结果:assignment、mission、duties、task。逐个选择,assignment主要侧重的是分配的意思。mission主要指使命,范围更大,而这里的任务主要是单个任务,范围小。duties的意思主要是职责、责任。相比较而言task比较适合,因此最终选择了task作为任务基类的名字。
在有些场景使用拼音也未尝不可。比如在保密系统中的“绝密”“机密”“秘密”这几个术语就没有很适合的英文单词对应。不妨就直接用拼音JueMi、JiMi、MiMi。
还要避免出现Chinglish单词。我曾经在一个开票系统的代码中看到一个函数名叫OpenInvoice。invoice是发票,但为什么要open它?琢磨了半天才悟出是“开票”的意思(我们把“开具发票”简称为“开票”)。这样晦涩的表达难以理解,而命名为MakeInvoice或直接用拼音KP(KaiPiao)都是更好的选择。
● 拼接短语
选好单词后,就把它们连在一起。我认为以下几种方式都是可行的。
另外C/C++中的宏定义都约定俗成地用大写字母表示,因此给宏命名时可以用大写字母加短横线的方式。
或许你还有自己的方式。但我的建议是:一旦选定了一种方式,就一直使用它,不要在自己的代码中出现多种风格。
● 简化
不要太迷信“代码自注释”这句话,对于一些长名字,可以做一些简化,比如函数。
可以简化为如下形式。
函数名用简写,在注释里说明全称。这样在调用该函数的地方,可以少敲几个字母。特别是对于局部变量,我推荐使用简写,如下所示。
pi的命名方式,比stProcessInformation更为简洁。请比较下面两段代码,你认为哪一种方式更易读一些?
局部变量用全拼。
局部变量用简写。
第一种方式需要阅读的信息太多。如果你采用这种方式,我猜测写这些变量的时候你多半是使用复制粘贴的方法,不会是一个个地敲字母,尽管很多IDE(集成开发环境)在输入时有联想功能。
● 忘掉匈牙利标记法
匈牙利标记法就是给变量名加个前缀,且为小写字母,用于标识该变量的类型。比如下面这样。
老一辈的程序员在Windows编程中都会使用匈牙利标记法。该标记法的产生有一定的时代背景,一方面因为老一代的代码编辑器功能都比较单一,如果函数比较长,阅读代码时想看某个变量的类型,需要向上滚屏回到变量定义的地方,这样操作很不方便。如果都使用匈牙利标记法,那么在变量出现的地方通过其前缀就能知道是什么类型。另一方面,如果在变量间赋值时,也能快速地发现变量间的类型是否匹配。
但如今,显示器的分辨率越来越高,这可以帮助我们在单屏里显示更多的代码。此外,一些新的代码编辑器都具有联想功能。比如在Source Insight中,当选中一个变量时,在Context窗口中就会显示该变量定义的地方,可以很方便地查看其类型。
至于要避免变量赋值时类型不匹配的问题,我认为最好的办法是交给编译器来完成,而不是用肉眼去查看。对于类型不匹配的赋值,编译器都会报错或给出告警,而你只需要留意编译告警(参考“法则11:留意编译告警”)。
因此,如今使用匈牙利标记法的理由已不再充分。另外,匈牙利标记法还有一个缺点,当改变某个变量的类型时,还需要对该变量出现的每个地方都修改其前缀。这违背了一次需求变更只集中修改一个地方的原则,而如果不使用匈牙利标记法就没有这个麻烦。
当然,使用匈牙利标记法也可以作为一种“个性”由软件设计师自行决定。只不过需要注意的是,一旦使用了该标记法,就要确保类型和前缀是一致的,以避免出现下面这类不一致的情况。这反而会造成误导,增加维护成本。
● 适时重构
实际上要做到第一点中所说的“用词准确”并不容易。可能会在过了很长一段时间后才想到一个更适合的名字。如果碰到这种情况,需要评估修改的工作量。如果这个名字是出现在接口中,其他子系统正在使用这个接口,那么就维持现状。因为如果要改动一个接口,使用这个接口的人也要进行相应的修改,这样改动工作量太大。如果仅仅是模块内部使用的一个变量或函数的名字,那可以在合适的时候进行修改,因为这样可以带来更好的可读性,易于他人理解。很多IDE都提供了方便的rename(重命名)功能,可以依靠这类工具来进行修改。不过需要注意的是,在注释里出现的名字不一定都能修改到,需要人工进行排查。
比如我曾经写过的一个Android小游戏“花样泡泡龙”[3],在给“小图形”命名时,我使用了drop一词作为类名。也曾考虑过用block一词,但我当时把block理解为“方块”,而这些图形里有三角形、五边形、圆形等其他形状,如图1-2所示。因此我认为笼统地叫“方块”并不合适。
图1-2 游戏积木的5种形状
并且由于它们都是从屏幕上方往下落,像“水滴”一样。因此把它们命名为正在“下降”的“水滴”(drop)。
时隔多年当我准备在本书中公开这部分源代码时,考虑到读者可能会对drop这个词感到不好理解,于是又重新考虑这个类的命名。最终发现其实之前考虑过的block一词是比较合适的,因为block还有“积木”的意思,而积木就可以有各种形状。因为当初没有注意到它的这个含义而没有采用。因此在最终发布的源代码中,所有用到drop一词的地方全都改成了block。此外bubble也是可以考虑的一个名字,其是“气泡”的意思,而气泡也可以有各种形状。