
通用代码技术
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].

参考资料
一篇博客: 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/
作者介绍

通用代码技术
公众号: 通用代码技术