几种变量命名规范和技巧

《重构: 改善既有代码的设计》的作者 Martin Fowler 在一篇博客中引用这样一段话:

There are only two hard things in Computer Science: cache invalidation and naming things.

计算机科学中只有两件难事:缓存失效和命名事物。

— Phil Karlton

由此可见,在计算机科学中命名一件事物是多么的困难。这当然也包括在代码中对变量、类型、类等进行命名。

命名的困难

第一、变量代表了一组状态,很多组状态的不断变化,构成了程序的逻辑。如何给这些状态起个又短小精准的名字,当然是比较难的。

第二、各种国产入门编程书籍从一开始就没有重视命名,书中到处都是类似于 x y 之类糟糕的命名,例如谭浩强的《C语言入门》。

第三、命名需要一定的英文功底,而国内程序员的英文水平参差不齐。有的直接把中文直译为英文来命名,有的甚至直接用拼音来命名。

种种这些都导致了在以后的大型项目中灾难式的命名方式,使得阅读和维护难如登天。

几种命名方式的建议

不要使用 abc, xyz

最经典最糟糕的例子就是使用 x y z 之类的不知所云的在数学中常用的变量来命名。

int a,b,c;
int x,y,z;

这类命名不会告诉你任何关于此变量的信息。

永远不要使用缩写

有许多国内外程序员都喜欢使用缩写。但是当你真正看到一个复杂函数使用缩写的时候,不看注释很难知道它在说什么。

public relScore(m1: Mov, mv2: Mov): number {
  const GW = 0.4;
  const YW = 0.1;
  const DW = 0.2;
  const WW = 0.3;
}

但你使用完整单词的时候,就可以轻松的读懂这段定义:这是一个关于电影评分关系的函数,那些那些变量是各种权重。

public movieRelationScore(movie1: Movie, movie2: Movie): number {
  const GENRE_WEIGHT = 0.4;
  const YEAR_WEIGHT = 0.1;
  const DIRECTOR_WEIGHT = 0.2;
  const WRITER_WEIGHT = 0.3;
}

我猜想这中现象应该是为了减少打字量而创造的,毕竟以前没有各种智能 IDE,没有代码补全。

但是都 2023 年了,古老的 Emacs 也可以自动补全了,所以请不要吝啬那一个字符。

不要将类型作为变量的前缀

如果您接触过老的 Windows 编程,那么您一定知道匈牙利命名法。它将类型作为变量名的前缀,看起来类似于这样:

int bIsValid; // bool
int iSpeed; // int32_t
int uNumUsers; // uint32_t
int szUserName; // char* 指针 

这在 C语言 的世界中非常常见,因为 C 只有最基本的几种数据类型,而类型前缀很明确的表达了它本身代表的类型。

但是目前大部分的静态类型语言都有了很丰富的类型系统,类型(type)很准确的告诉您正在查看的内容是什么。

bool bIsValid;
int32_t iSpeed;
uint32_t uNumUsers;
char* szUserName;

所以不需要再将类型作为变量名前缀了。

将单位放到变量名中

这是一个接受延迟时间的函数:

void execute(int delay)

如果 delay 以秒为单位的话,应该将此变量命名为 delaySeconds,这样用户就可以知道这里对应的是秒还是毫秒。

但是在有些语言中,时间被定义为了一种内置类型,这样以来就不必再需要将单位放到变量名中。例如在 C# 中:

void execute(TimeSpan delay)
{
  double seconds = delay.TotalSeconds;
}

但是对于像 Python 这样的动态类型语言,就不能依赖类型声明来告诉我们关于单位的信息,此时就需要将单位放到变量名中。

class Renderer:
  def __init__(self, renderIntervalSeconds):
    self.renderIntervalSeconds = renderIntervalSeconds;

不要使用Base 或 Abstract 命名一个类

这种命名方式不会出现在任何一种语言的标准库中,因为它是一种糟糕的命名方式。

作为例子,如果有一个 Truck 卡车类:

public Truck {
  private:
    double weight;
  public:
    Truck(double weight): weight(weight);
}

我想创建一个它的父类,那么我永远不会将它命名为 BaseTruck。因为这是没有意义的,BaseTruck 不能告诉我任何信息。

假如将Truck的父类命名为 Car 的话,那么在下面的函数中,可以轻松的知道它写的是什么:

public Truck : Car {
...
}

public void addContestant(Car car) {
}

此时将一个 Truck 的对象传入 addContestant的参数中,不需要任何信息就可以知道这是合理的,因为卡车是汽车的一种。

CodeIf 工具介绍

Codelf 通过搜索在线开源平台 Github , Bitbucket , Google Code , Codeplex , Sourceforge, Fedora Project 的项目源码,帮开发者从中找出已有的匹配关键字的变量名,从而帮助开发者命名变量。

Snipaste 2023 02 04 01 19 36

搜索 用户

Snipaste 2023 02 04 01 22 14

结语

关于命名的哲学和技巧数不胜数,这里只是列举了几种最常见的命名规范。有兴趣的话,可以阅读 Google 开源项目风格指南,英语好的可以直接看原版 Google Style Guides.

类似文章

一条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注