关于编程中的命名

许多人会奇怪我在各种的想法中,会把命名问题看的这么重,可对于我恰是如此。做程序员这么长时间,我不断地学习实践、思考感受过程中,慢慢的将好的名字作为程序开发最重要最基础的要素之一。

 

对此多数人会不以为然,一部分人认为命名虽然重要但不至如此,不过对于我,命名总是程序中首要注意的东西,比诸如接口设计、类设计、设计模式等更注重。个人而言我更愿意把著名的公式 程序 = 数据结构 + 算法 改成 程序 = 数据结构 + 算法 + 命名 ,这能充分反映出我对其重要性的看法。前者我认为是对机器而言的程序,后者才是对人而言的。当源代码经编译器变成机器代码并去除符号表,或者.NET程序被混淆器处理,所有变量函数名等全部变成无意义的符号,程序便成了大部分人无法理解的东西。失去了有意义的名称,由汇编代码或IL代码反向理解来自第三方的整个程序变得十分困难甚至不可能,需要拥有专门的知识和经过长期训练。相信大部分程序员都不会有兴趣做这类事情,实际的需求也使得相关群体仅局限于黑客,破解者,反病毒程序员、编译器作者、专业研究人员等。

 

抛开上面的极端情形,大部分人的日常开发所要面对的是给各种程序元素起名字,名字空间、类、接口、名字空间、函数、局部变量、成员变量…。这个活动如此频繁常见,大部分人不会有意识的关注,但对于其重要性和好处,我们可以从一些业界的经典书籍中得到感受。《代码大全》中花费了整整第11章(The Power of Variable Names)来讲述变量名的重要性、注意事项、各种实践经验技巧等,7.3 节(Good Routine Names)则涉及到了函数命名。32.2节(Programming Style as Documentation)中反映了好的命名对于自说明代码中的意义,34.3节 (Write Programs for People First, Computers Second)则是表明了要起好名字的根本原因,即可读性。《重构》一书中提到的不少方法都直接或间接涉及到命名问题,如10.1节(Renaming Method)对方法重命名这一“最简单也最重要的一件事”做了论述,6.1节(Extract Method)也能一再表达了作者对于命名的强调。《NET 设计规范——.NET约定、惯用法与模式》中提到了框架开发的原则之一是一致性,而读者在第3章可以看出为了一致性原则在命名方面做了很多努力,作者说:“The team that develops the .NET Framework Base Class Library spends an enormous amount of time on naming and considers it to be a crucial part of framework development”。事实上当我写作本文重新回顾这些段落时,深感其内容的分量,而下面的讨论其实也不会超出作者们指点出的范畴。

 

好名字的根本原因来自在于我们写程序的一个基本原则,即不仅是为了让机器运行得到期望的结果,还要为了让别的程序员能容易的理解、维护及改进。程序员之间交流大脑深处的思维的最终手段便是阅读代码,代码中的名字是如此之多,以至于为每个名字在大脑里多花费一点点记忆和追踪,整个程序理解的成本就会明显增加。长期阅读过大量代码的人我相信都能感受到好的命名为其节省的精力和时间。当我说“别的程序员”时,其实不仅仅是不同于作者的别人,也可能是一段时间后的作者本人。大家有过这样的经历吗,很长时间后出于某个原因忽然要重读自己写过的代码,写代码时的记忆已经荡然无存,这时似乎是在看另一个不同的人跟你对话,如果程序很好读,会很欣慰,反之则暗骂那个“别的程序员”——曾经的自己。

 

再次推荐大家阅读《重构》10.1(Renaming Method)中的Motivation段落。对于很多程序员,其实不太会给出特别糟糕的命名,但相当好的名字也不容易,通常都是起个“差不多”的就可以了。并且值得一提的是,每个“差不多”的名字一旦诞生,多数人基本不会再去考虑随着代码的变化而改变它,甚至直到这个名字在相应环境中变得“实在说不过去”。而我常常愿意多花一点时间来想一个比“差不多”要更好一点的名字,有时甚至反复琢磨,很有点“推敲”的意思。在工作中的Code Review,我总是会先关注命名并经常给出建议,程序发生变化时,我也愿意有意识的审视代码中的相关名字是否应随之改进。现在的主流集成编程环境几乎都支持Rename这个最简单的重构手段,操作简单,比直接文本替换安全,我们应该充分的利用它。好名称起到的一个重要的作用是减少了注释和文档的数量,即让代码变得“自说明”。我非常不理解有些人愿意花时间写注释和文档,却没认真想过很多情况下用一些更好的名字来改进代码是多么有益,阅读的人可以节省阅读额外资料,作者本人则降低了维护代码与注释文档一致性的成本。

 

本文一开始我的标题叫“关于程序中的命名”,后来把“程序”改成了“编程”。因为除了狭义的程序代码中的命名,推广到更大的范围,编程活动中其他很多元素的命名也是一样重要,比如数据库的库名,表名列名,代码的目录名,配置文件中的配置项名称,等等。

 

命名过程中往往碰到含义与长度的矛盾。就个人目前的编程实践,在名字的含义清晰和名字的长度控制间如果不能很好的平衡,一般而言我更倾向用长一点的名字而保持含义清楚。以前的年代,似乎大家都愿意把名字起的越短越好,不可否认主要是一些历史原因导致了这个习惯。那时候的控制台屏幕,80列*25行文本界面,可显示内容太少,加之内存容量小,硬件处理能力低,很多编程环境从设计上就限制的名字最大长度,这样短小的名字会有显而易见的好处甚至是必须的。可是时代在变化,如今我们使用的是物美价廉的高分辨率屏幕,内存容量等硬件处理能力及开发环境更是没有约束,与之对应的是人的习惯却不容易立刻随着改变,很多人仍然不自觉的尽量使用短小的名称,为了省一些字符或键盘的敲击次数把名字搞得难以理解。对于频繁出现的过长名字,个人觉得可以考虑用约定的办法,规范一些项目或团队范围的前缀后缀等来缩短长度。

 

最后,具体到命名方面的实践,我很乐意分享的一个小经验是:对于表示时间段的变量,总是在名字中加入时间单位,比如:periodMilliseconds,waitMinutes,等。这样的命名帮助我很容易的避免时间单位不一致造成的土错误(很多年以前的一次调程序我花费了一个小时找出这样的原因),比如调用 Sleep()函数,变量名里没有单位时,总是要去靠人为跟踪代码来确信参数没有传错,因为这样的问题编译器无法检测(当然在.NET里我倾向于总是用更好的强类型变量TimeSpan)。而在时间点的变量中,如果类型里没有时区信息(像DateTimeOffset类就包含了时区信息),我总是愿意把时区信息加到名字里,比如lastUpdateUtcTime, createLocalTime,等等。其实这个问题扩大到更一般的层面,是关于物理量的数据处理,物理量是由数值加单位组成的,更有效的方法应通过强类型化而不是命名来解决。像F#中的Units of Measure就很好,虽然我仍觉得可以改进,无论如何,这已经是完全不同的话题了。

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页