代码整洁之道-读书笔记之有意义的命名

1. 有意义的命名

1.1 介绍

软件中随处可见命名。我们给变量、函数、参数、类和封包命名。我们给源代码及源代码所在目录命名。我们给jar文件、war文件和car文件命名。我们命名、命名,不断命名。既然有这么多命名要做,不妨做好它,下文列出了取个好名字的几条简单规则。

1.2 名副其实

变量、函数或类的名称应该已经答复了所有的大问题。它应该告诉你,他为什么会存在,做什么事,应该怎么用。如果名称需要注释来补充,那就不算名副其实

看个例子

错误的写法

int d://消逝的时间,以日计

名称d什么也没说明。它没有引起对时间消逝的感觉,更别说以日计了。我们应该选择指明了计量对象和计量单位的名称:

正确的写法

int elapsedTimeInDays; // 已用天数
int daysSinceCreation; // 创建以来的天数
int daysSinceModification;  // 修改后的天数
int fileAgeInDays; // 文件日期

选择体现本意的名称能让人更容易理解和修改代码。下列代码的目的何在?

public List<int[]> getThem() {
  List<int[]> listl = new ArrayList<int[]>(); 
  for(int[] x: theList) {
    if (x[0]==4) 
      listl.add(x); 
    return listl; 
  }
}

为什么难以说明上列代码要做什么事?里面并没有复杂的表达式。空格和缩进中规中矩。只用到三个变量和两个常量。甚至没有涉及任何其他类或多态方法,只是(或者看起来是)一个数组的列表而已。

问题不在于代码的简洁度,而是在于代码的模糊度:即上下文在代码中未被明确体现的程度。上列代码要求我们了解类似以下问题的答案:

(1)theList中是什么类型的东西?

(2)theList零下标条目的意义是什么?

(3)值4的意义是什么?

(4)我怎么使用返回的列表?

问题的答案没体现在代码段中,可那就是它们该在的地方。

比方说,我们在开发一种扫雷游戏,我们发现,盘面是名为theList的单元格列表,那就将其名称改为gameBoard.

盘面上每个单元格都用一个简单数组表示。我们还发现,零下标条目是一种状态值,而该种状态值为4表示“已标记”。只要改为有意义的名称,代码就会得到相当程度的改进:

public List<int[]> getFlaggedCells(){
  List<int[]> flaggedCells = new ArrayList<int[]>();
  for (int[] cell : gameBoard){
    if (cel1[STATUS_VALUE] == FLAGGED){
      flaggedCells.add(cell):
      return flaggedCells
    }
  }
}

注意,代码的简洁性并未被触及。运算符和常量的数量全然保持不变,嵌套数量也全然保持不变。但代码变得明确多了。

还可以更进一步,不用int数组表示单元格,而是另写一个类。该类包括一个名副其实的函数(称为isFlagged),从而掩盖住那个魔术数'。于是得到函数的新版本:

public List<Cell> getFlaggedCells(){
  List<Cell> flaggedCells = new ArrayList<Cell>();
  for (Cell cell : gameBoard){
    if (cel1[STATUS_VALUE] == FLAGGED){
      flaggedCells.add(cell):
      return flaggedCells
    }
  }
}

1.3 避免误导

程序员必须避免留下掩藏代码本意的错误线索。应当避免使用与本意相悖的词。例如,hp、aix和sco都不该用做变量名,因为它们都是UNIX平台或类UNIX平台的专有名称。即便你是在编写三角计算程序,hp看起来是个不错的缩写2,但那也可能会提供错误信息。

别用accountList来指称一组账号,除非它真的是List类型。List一词对程序员有特殊意义。如果包纳账号的容器并非真是个List,就会引起错误的判断3。所以,用accountGroup或bunchOfAccounts,甚至直接用accounts 都会好一些

提防使用不同之处较小的名称。想区分模块中某处的 XYZControllerForEfficientHandlingOfStrings 和另一处的XYZControllerForEfficientStorageOfStrings,会花多长时 间呢?这两个词外形实在太相似了。

误导性名称真正可怕的例子,是用小写字母1和大写字母O作为变量名,尤其是在组合使用的时候。当然,问题在于它们看起来完全像是常量“壹”和“零”。

int a = 1;
if (O==l) {
  a = O1; 
}else {
  l = 01;
}

1.4 做有意义的区分

以数字系列命名(al、a2,···aN)是依义命名的对立面。这样的名称纯属误导—完全没有提供正确信息:没有提供导向作者意图的线索。试看:

错误的写法

public static void copyChars(char al[],char a2[]){
  for (int i= 0;i<al.length;i++){ 
    a2[i]=al[i]:
  }
} 

正确的写法

public static void copyChars(char source[],char destination[]){
  for (int i= 0;i<source.length;i++){ 
    destination[i]=source[i]:
  }
} 

废话是一种没意义的区分。假设你有一个Product类,如果还有一个ProductInfo或者ProductData类,他们命名虽然不同,但是意思却无区别

废话都是冗余。Variable(变量)一词永远不应当出现在变量中;Table一词永远不应该出现在表明中;nameString会比name好吗?

命名一定要有意义,否则就会使代码变糟糕

1.5 使用读得出来的名称

不要自己造词,要使用英语单词或者短语

1.6 使用可搜索的名称

试图消除魔法值,将魔法值提取到常量中,常量起名一定要见明知意

1.7 避免使用编码

编码已经太多,无谓再自找麻烦。把类型或作用域编进名称里面,徒然增加了解码的负担。没理由要求每位新人都在弄清要应付的代码之外(那算是正常的),还要再搞懂另一种编码“语言”。这对于解决问题而言,纯属多余的负担。带编码的名称通常也不便发音,容易打错。

1.7.1 成员前缀

错误的写法

private String m_dsc:// The textual description 
void setName(String name){
  m_dsc = name; 
}

正确的写法

String description;
void setDescription(String description){
   this.description-description;
}

1.9 类名

类名和对象名应该是名称或者名称短语,如Customer、WikiPage、Account和AddressParser,避免使用Manager、Processor、Data或者Info这样的类名,类名不应当是动词。

1.10 方法名

方法名应当是动词或者动词短语,如postPayment、deletePage或save。

1.11 别扮可爱

命名不要用稀奇古怪的词语,要用常用的,例如HolyHandGrenade(手榴弹),没有人知道是干嘛的,不过DeleteItems或许是更好的名称,别用whack(重击)来表示kill(),别用eatMyShorts(吃短裤)这类和文化紧密相关的笑话来表示abort();

1.12 每个概念对应一个词

给每一个抽象概念选一个词,并且一以贯之。

在同一堆代码中有controller,又有 manager,还有driver,就会令人困惑。DeviceManager 和Protocol-Controller 之间有何根本区别?为什么不全用 controllers 或 managers?他们都是 Drivers吗?这种名称,让人觉得这两个对象是不同类型的,也分属不同的类。

对于那些会用到你代码的程序员,一以贯之的命名法简直就是天降福音。

1.13 别用双关语

避免将同一个单词用于不同的目的。同一术语用于不同的概念就,基本上就是双关语了。

1.14 使用解决方案领域名称

记住,只有程序员才会读你的代码。所以,尽管用那些计算机科学(Computer Science, CS)术语、算法名、模式名、数学术语吧。依据问题所涉领域来命名可不算是聪明的做法,因为不该让协作者老是跑去问客户每个名称的含义,其实他们早该通过另一名称了解这个概念了。

对于熟悉访问者(VISITOR)模式的程序来说,名称AccountVisitor富有意义。哪个程序员会不知道JobQueue的意思呢?程序员要做太多技术性工作。给这些事取个技术性的名称,通常是最靠谱的做法。

1.15 使用源自所涉问题领域的名称

如果不能用程序员熟悉的术语来给手头的工作命名,就采用从所涉问题领域而来的名称吧。至少,负责维护代码的程序员就能去请教领域专家了。

优秀的程序员和设计师,其工作之一就是分离解决方案领域和问题领域的概念。与所涉问题领域更为贴近的代码,应当采用源自问题领域的名称。

1.16 添加有意义的语境

很少有名称是能自我说明的—多数都不能。反之,你需要用有良好命名的类、函数或

名称空间来放置名称,给读者提供语境。如果没这么做,给名称添加前缀就是最后一招了。

设想你有名为firstName、lastName、street、houseNumber、city、state和zipcode的变量。 当它们搁一块儿的时候,很明确是构成了一个地址。不过,假使只是在某个方法中看见孤零零一个state变量呢?你会理所当然推断那是某个地址的一部分吗?

可以添加前缀 addrFirstName、addrLastName、addrState等,以此提供语境。至少,读者 会明白这些变量是某个更大结构的一部分。当然,更好的方案是创建名为Address的类。这样,即便是编译器也会知道这些变量隶属某个更大的概念了。

错误的写法

private void printGuessstatistics(char candidate,int count){// 打印统计数据
  String number;
  String verb;
  String pluralModifier; 
  if (count ==0) {
    number="no"; 
    verb ="are";
    pluralModifier = "s"; 
  }else if (count==1){ 
    number="1";
    verb="is";
    pluralModifier =""; 
  }else{
    number=Integer.toString(count);
    verb="are";
    pluralModifier="s"; 
  }
  String guessMessage=String.format("There %s %s %s%s", verb, number, candidate,pluralModifier );
  print(guessMessage); 
}

正确的写法

public class GuessStatisticsMessage {
  private String number;
  private String verb;
  private String pluralModifier:
  public String make(char candidate, int count){ 
    createPluralDependentMessageParts(count);
    return String.format("There %s %s %s%s",verb, number, candidate,pluralModifier); {
  private void createPluralDependentMessageParts(int count){
    if(count== 0){
      thereAreNoLetters(); )
    }else if(count==1){ 
      thereIsoneLetter(); 
    }else{
      thereAreManyLetters(count); 
    }
  private void thereAreManyLetters(int count){
    number=Integer.toString(count);
    verb="are";
    pluralModifier = "s"; 
  }
  private void thereIsoneLetter(){
    number="1";
    verb="is";
    pluralModifier="";
  }
  private void thereAreNoLetters(){
    number="no";
    verb="are";
    pluralModifier="s"; 
  }
}

1.17 不要添加没用的语境

设若有一个名为“加油站豪华版”(Gas Station Deluxe)的应用,在其中给每个类添加GSD前缀就不是什么好点子。说白了,你是在和自己在用的工具过不去。输入G,按下自动完成键,结果会得到系统中全部类的列表,列表恨不得有一英里那么长。这样做聪明吗?为什么要搞得IDE没法帮助你?

再比如,你在GSD应用程序中的记账模块创建了一个表示邮件地址的类,然后给该类命名为 GSDAccountAddress。稍后,你的客户联络应用中需要用到邮件地址,你会用GSDAccountAddress吗?这名字听起来没问题吗?在这17个字母里面,有10个字母纯属多余和与当前语境毫无关联。

只要短名称足够清楚,就要比长名称好。别给名称添加不必要的语境。

对于Address类的实体来说,accountAddress和 customerAddress 都是不错的名称,不过用 在类名上就不太好了。Address是个好类名。如果需要与MAC地址、端口地址和Web地址相区别,我会考虑使用PostalAddress、MAC和URI。这样的名称更为精确,而精确正是命名的要点。

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
代码整洁之道-读书笔记之有意义的命名
软件中随处可见命名。我们给变量、函数、参数、类和封包命名。我们给源代码及源代码所在目录命名。我们给jar文件、war文件和car文件命名。我们命名、命名,不断命...
<<上一篇
下一篇>>