通用代码技术

V1

2023/02/04阅读:21主题:雁栖湖

代码中的命名小技巧

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

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

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

-- Phil Karlton

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

Part1命名的困难

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

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

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

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

Part2几种命名方式的建议

1不要使用 abc, xyz

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

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

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

2永远不要使用缩写

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

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 也可以自动补全了,所以请不要吝啬那一个字符。

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

如果您接触过老的 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;

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

4将单位放到变量名中

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

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;

5不要使用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的参数中,不需要任何信息就可以知道这是合理的,因为卡车是汽车的一种。

Part3CodeIf 工具介绍

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

搜索 用户

Part4结语

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


参考资料

[1]

一篇博客: https://martinfowler.com/bliki/TwoHardThings.html

[2]

Codelf: https://unbug.github.io/codelf/

[3]

Google 开源项目风格指南: https://zh-google-styleguide.readthedocs.io/en/latest/

[4]

Google Style Guides: https://google.github.io/styleguide/

分类:

后端

标签:

后端

作者介绍

通用代码技术
V1

公众号: 通用代码技术