本文是「Clean Code」(英文版)第二章的读书笔记。
第二章简单地列举了一些命名规则,我们在coding的时候会不断地对我们的变量、函数、参数、类、package,甚至源文件、和包含源文件的目录等等进行命名,这里是简单的几个命名规则能帮助你更好地对这些命名。
1. 使用名副其实(Intention-Revealing)的名字
不要定义无意义的名字
不要使用magic numbers
-
例子:
// 坏代码例子 public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>(); for (int[] x : theList) if (x[0] == 4) list1.add(x); return list1; } //不是很坏的代码 public List<int[]> getFlaggedCells() { List<int[]> flaggedCells = new ArrayList<int[]>(); for (int[] cell : gameBoard) if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell); return flaggedCells; } //好代码 public List<Cell> getFlaggedCells() { List<Cell> flaggedCells = new ArrayList<Cell>(); for (Cell cell : gameBoard) if (cell.isFlagged()) flaggedCells.add(cell); return flaggedCells; }
2. 避免传达错误的意思
避免使用有歧义的名字,比如与其他已存的命名系统相同或相似的名字(比如hp, aix, sco)
避免使用与语言特性相关的词,比如,accountList, 如果它真的是一个某些语言中的List结构还好,如果不是,最好使用bunchOfAccounts或者直接accounts更好。
-
避免在不同的地方使用只有微妙区别的命名。比如你要花多久才能区分一下两个变量:
XYZControllerForEfficientHandlingOfStrings XYZControllerForEfficientStorageOfStrings
使用相似的命名来代表相似的概念。这一条和上一条并不冲突,相似的概念应该使用相似但可以有明显的区分度的命名进行表达。
避免使用外形上很容易造成误解的命名:比如大写的o和小写的L,它们和0和1特别像,很难区分。
3. 在命名和命名之间使用有意义的区别
比如:在一段代码中如果需要使用两个相似概念的变量时,尽量使用有意义的方式来区别它们,避免使用a1、a2等变量命名。
避免使用不必要的“噪音字”。一个类名叫Product比叫ProductInfo或者ProductData等好多了。
避免使用冗余的’噪音字’。变量名中永远不应该包含单词variable;表名中应当永远不包含table。
-
这里是一个错误的示例,这三个函数的命名完全是没有意义的
getActiveAccount(); getActiveAccounts(); getActiveAccountInfo();
4. 使用语音可读的命名
- 一个好的命名应该是可读的,可读的命名能帮助人更好理解代码,同时使人们更容易去讨论这些代码。
- 尽量避免使用genymdhms(generation date,year, month,打野,后人,minute,and second)这样很难读出来的命名,而使用generationTimeStamp
5. 使用容易搜索的命名
这条其实还是说要使用有意义的命名,比如一个变量MAX_CLASSES_PER_STUDENT是7,就不要把这个变量的名字定 义为SEVEN,这样更方便程序的读者去搜索。
作者在这里写道,单个字母的变量名(i, j, k等)应该只能作为一个很短的方法的内部变量使用。
6. 避免发明新的编码方式来命名
匈牙利命名法:由于现代语言对于命名的长度已经没有限制,所以像匈牙利命名法这种对命名进行二次编码的方式应该尽量避免使用
-
前缀:很多语言使用前缀m_来标识这个变量是一个成员变量,这种做法是没有意义的,程序的读者经常会无视你的前缀而只关注后边的内容。另外,你的类和方法的定义范围应该足够小,让你不需要使用这一的前缀
public class Part { private String m_dsc; // The textual description void setName(String name) { m_dsc = name; } } public class Part { String description; void setDescription(String description) { this.description = description; } }
接口和实现的命名:作者建议接口名尽量不要使用前缀,比如IShapeFactory作为接口,ShapeFactory作为实现类。应该避免让这个接口的使用者看到给他使用的是一个接口。
7. 避免脑补
- 避免写出晦涩难懂的命名,比如在某处定义一个变量名为r,而只有写代码的人知道这个r代表的是url的小写形式。
- 类名:类名应当是一个名词或名词短语,而不是动词
- 方法名:方法名应当是一个动词短语;当构造器需要重载时,尽量使用不同命名的工厂方法,而不是使用重载的不同参数的构造器(这点我是持保留意见的)。
8. 不要使用可爱的命名
9. 同一个概念使用同一个单词
- 避免在一个工程中使用fetch、get、retrieve等相似的单词来表达相同的意思。
- 相同的,还有经常使用到的Manager、Controller、Driver等。
10. 避免使用双关语
- 同一个单词在两个不同的场景下被使用,往往会造成误解,尤其是这两种场景下它们的意思还是不相同的。
11. 使用程序员熟悉的专有名词
- 不要害怕使用一些专有的计算机名词,比如算法,设计模式,数学名词等等。
12. 使用描述问题的命名
- 如果没有一个计算机专业名词来描述你的方法或者变量,尽量保证你的命名可以清晰描述你的问题。
13. 添加有意义的上下文
- 写程序就像写文章,每一个方法或者类都像一个段落,一些命名他们本身并没有很明显的含义,那么就需要把它放在一个有意义的上下文中,比如一个地址类Address,里边有一个属性是state,就明显是标识州的意思,但是在别的上下文可能会有不同的含义。
14. 不要添加无意义的上下文
- 作者建议尽可能使用短的名字(在能清晰表达它意思的前提下)
- 作者在这里举的无意义的上下文的例子有:在一个名为“Gas Station Deluxe”的程序中,所有的类都加上GSD的前缀,这样的上下文是无意义的(这里让我想到了Objective-C的GCD,哈哈)