几种变量命名规范和技巧
《重构: 改善既有代码的设计》的作者 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 的项目源码,帮开发者从中找出已有的匹配关键字的变量名,从而帮助开发者命名变量。
搜索 用户
:
结语
关于命名的哲学和技巧数不胜数,这里只是列举了几种最常见的命名规范。有兴趣的话,可以阅读 Google 开源项目风格指南,英语好的可以直接看原版 Google Style Guides.
一条评论