# 05 | 经验总结:如何给你的代码起好名字? 上一节我们讲了编码规范的重要性,而编码规范,从起一个好名字开始。但起名字,也没有我们想得那么简单。有个流传很广的戏言:“计算机科学只有两件难事,废弃缓存和取名字。” 之所以说是戏言,因为取名字这件事无论如何都不算是高深的学问;之所以广泛流传,因为取名字真的就是一件很难的事情,而且起名字是关乎代码质量的大事。 给小孩取过名字的人都知道,取个好名字有多难,又要合八字,又要算五行,还要避尊者讳。 写程序给代码取名字更难,每天都要想很多名字。给孩子取名字,父母喜欢就行,给代码取名字,还要别人也喜欢。 ## 为什么需要一个好名字? 名字要准确地代表它背后的东西,并且还能让代码干净漂亮。不然,我们的思路就会受到干扰,影响我们的思考和心情。 比如说,对于答案只有是与非两个选择的时候,我们通常使用布尔类型(boolean)。所以,取名字的时候,我们通常需要一个表达疑问的前缀,比如是不是“is”。 ``` public boolean isEmpty(); // String.isEmpty() ``` 但如果我们把这样的疑问前缀,使用到一个非布尔类型上,会有什么效果? ``` public byte[] isEmpty(); ``` 你是不是觉得如鲠在喉,对于代码要干什么百思不得其解? 反正,我写这个例子的时候,感觉像是吃了五百只苍蝇! 名字就是沟通的方式,错误的命名很难让我们清楚地理解代码真实的意图。所以,混淆的命名很难让我们阅读和理解代码。 虽然编译器不关心命名的好坏,但是我们却可以从一个好名字中获得巨大的好处。 ## 为什么需要命名规范? 虽然起一个好名字的重要性不言而喻,但命名规范的选择,以及执行程度,却是一个有争议的话题。有人喜欢这种规范,有人喜欢那种规范,有人干脆认为规范都太教条,真是众口难调。此外,即使已知且明确定义了命名规范,某些组织也无法始终如一地遵守它们,从而导致不一致和混淆。如果命名规范内部不一致,任意且难以记忆,这些挑战还会加剧。 所以使用一个好的命名规范是非常重要的,我们都能获得哪些好处呢? 1. 为标识符提供附加的信息,赋予标识符现实意义。帮助我们理顺编码的逻辑,减少阅读和理解代码的工作量; 2. 使代码审核变得更有效率,专注于更重要的问题,而不是争论语法和命名规范这类小细节,提高开发效率; 3. 提高代码的清晰度、可读性以及美观程度; 4. 避免不同产品之间的命名冲突。 ## 有哪些常见的命名方法? 尽管不同的编程环境、不同编程语言也需要沟通,但遗憾的是,到目前为止,还没有一种通用的命名方法。 在不同的场景下,程序员们有着不同的偏好。我们需要阅读很多代码,多了解一些命名方法,这样我们才能更好地理解不同风格的代码。 我来一一介绍下几种常见的命名方法。 **1.驼峰命名法(CamelCase)** 驼峰命名法指的是使用大小写混合的格式,单词之间不使用空格隔开或者连接字符连接的命名方式。它有两种格式:大驼峰命名法(UpperCamelCase)和小驼峰命名法(lowerCamelCase)。 大驼峰命名法的第一个单词以大写字母开始,其余的和小驼峰命名法相同。 比如:LastName, InputStream。 小驼峰命名法的第一个单词以小写字母开始,其他单词以大写字母开始,其余字母使用小写字母。 比如:firstName, toString。 有时候,一个名字可能有不只一种合理形式,比如缩略语(IPv6)或者异常的结构(iOS)。 为了减少这种不确定性,Google定义了以下的转换规则: 1. 从正常的表达形式开始,把短语转换成ASCII码,并且移除单引号。 例如,“Müller’s algorithm”转换为“Muellers algorithm”; 2. 如果上述结果含有其他标点符号,比如连字符,在该符号处,把这个结果切分成单词形式。 如果某个单词已经是驼峰形式,也相应地切分开来。 例如,“AdWords”切分成“ad words”,“non-current assets”切分成“non current assets”; 3. 将所有字母转换为小写字母,然后将每个单词的首字母大写,这样就得到了大驼峰式命名的形式; 如果第一个单词的首字母小写,就得到了小驼峰式命名的形式; 4. 将所有的单词连在一起,就是最后的标识符命名。 下面的表格列出了不同例子的正确转换形式,和容易出错的转换形式 (出自“Google Java Style Guide”)。 ![](https://static001.geekbang.org/resource/image/f2/1d/f28217dc672df8bc968eccb57ce19c1d.png) **2.蛇形命名法(snake\_case)** 在蛇形命名法中,单词之间通过下划线“\_”连接,比如“out\_of\_range”。 **3.串式命名法(kebab-case)** 在蛇形命名法中,单词之间通过连字符“-”连接,比如“background-color”。 **4.匈牙利命名法** 在匈牙利命名法中,标识符由一个或者多个小写字母开始,这些字母用来标识标识符的类型或者用途。标识符的剩余部分,可以采取其他形式的命名法,比如大驼峰命名法。 如果起始的小字母用来表示标识符的数据类型,这种命名法也被称为系统匈牙利命名法。 比如: * lAccountNum标识一个\_长整数\_(首字母“l”,long)。 * szName标识一个\_零字符结束的字符串\_(首字母“sz”,zero-terminated string)。 如果起始的小字母用来表示标识符的实际用途,这种命名法也被称为应用匈牙利命名法。 比如: * rwPosition标识一个\_行\_(首字母“rw”,row)。 * usName标识一个\_非安全字符串\_(首字母“us”, unsafe string)。 由于在微软产品中的广泛使用,匈牙利命名法曾经是一种流行的命名形式。然而,由于这种命名会带来不必要的记忆负担和阅读障碍,导致命名规则的执行和名称的维护都很困难,微软已经抛弃了这种命名形式。 由于历史的原因,还有很多代码使用这种命名形式。阅读这些代码时,你可以选择性地忽略这些表示类型或者用途的字母前缀。 ## Java命名规范 一段代码,是不是只能使用一种命名方法? 一般来说,一个编码规范会组合使用这些命名方法,每一种命名方法都被规定了适用的范围。 这样就形成了命名规范。 比如,Java的命名规范可以使用下表来表示。 ![](https://static001.geekbang.org/resource/image/19/b1/19cd98970ceceaed8247a586ba3895b1.png) 需要注意的是,常量必须是真的不能改变的量,不打算改变或者能够改变的量都不能算作常量。 比如,下面的例子声明的是常量: ``` static final short MAX_VALUE = 32767; static final Set EMPTY_NAMES = Collections.unmodifiableSet(Collections.emptySet()); ``` 下面的例子声明的就不是常量,它们的值都可以改变: ``` static short nonFinalShort = 32767; static final Set mutableNames = Collections.emptySet(); static final String[] names = { "Alice", "Bob", "Tom" }; ``` 需要注意的是,方法标识符使用动词或者动词短语,这是传统的方法命名。如果能够分隔开配置(set)和使用(get),使用名词的方法标识符。比如Builder模式的接口设计。这个接口设计和命名惯例,我们以后再讨论。 ## 怎么取好名字? 了解了命名方法后,你是不是想知道怎么取好名字呢?一般来说,给代码取名字,需要遵守如下三条原则。 **1.要有准确的意义** 名字要能够准确、完整地表达出它代表的意义,可以见字知意,名副其实。 比如,表达式“a = b - c”的语法是没有什么问题,可是该表达式代表的实际含义并不清楚。相比而言,“grossIncome = grossRevene - costOfGoodsSold”就有很准确、清晰的现实意义。这样的命名更容易阅读和理解。 ![](https://static001.geekbang.org/resource/image/07/43/077720a9965c6daf354a3bc2518e4843.png) **2.严格遵守命名规范** 不同的编程环境,偏爱不同的命名规范,比如Java倾向于使用驼峰命名法,C语言倾向于使用蛇形命名法,CSS使用串式命名法。 尽管如此,如果定义了个性化的命名规范,请严格遵守自定义的命名规范,如果没有定义个性化的命名规范,我们就需要严格遵守业界普遍公认的命名规范。 ![](https://static001.geekbang.org/resource/image/75/e3/7510f9e45dffa6664c410d0e72b534e3.png) **3.可读性优先** 名字的可读性一定要优先考虑,一般需要注意以下几点。 * 可读性强的名字优先于简短的名字,尽量使用完整的词汇。 * 不要使用缩写、简写、缩略词,除非这些词语被广泛使用。 * 不要使用太短的名字,比如一个字母,除非是广泛接受的特例(i/j/k/m/n表示临时使用的整数,c/d/e表示临时使用的字符)。 * 避免含糊、混淆或者误导。 另外,不要混合使用英文和汉语拼音。由于很多类库使用的是英文,如果使用汉语拼音命名,会造成事实上的拼音名字与英文名字的混用,所以也要尽量避免使用拼音命名。 ![](https://static001.geekbang.org/resource/image/99/16/99fbf25bc5c0aedb88cefe76c67d7816.png) ## 小结 简言之,取名字要做到“信、达、雅”(准确、直观、优美)。“信”和“达”是基本要求,有才气的你可以有“雅”的追求。 取好名字是编写优秀代码最基础也是最重要的一项修炼。 你不妨试试上述的原则和规范,将它们用于新代码,或者整理老代码。 仅仅因为名字的优化,你就会立刻感受到代码质量的大幅度提升! ## 一起来动手 所以为了让你更好地实践,我找了一段Java代码。你来试试,这段代码中有哪些名字可以优化? 欢迎你把优化的代码发在评论里,我们亲自感受下如何优化代码名字。 ``` import java.util.HashMap; import java.util.Map; class Solution { /** * Given an array of integers, return indices of the two numbers * such that they add up to a specific target. */ public int[] twoSum(int[] nums, int target) { Map map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[] { map.get(complement), i }; } map.put(nums[i], i); } throw new IllegalArgumentException("No two sum solution"); } } ``` 备注:代码选自[https://leetcode.com/problems/two-sum/](https://leetcode.com/problems/two-sum/) 你也可以把这篇文章分享给你的朋友或者同事,一起来讨论一下这道小小的练习题。