命名是主观的、情境性的,它是一种艺术,就像大多数艺术一样,我们会发现规律。通过阅读他人的代码,我学到了很多东西。在这篇文章中,我整理了9条指南,希望其他人在我阅读他们的代码时能够遵循这些指南。
当软件工程师打开一个类时,她应该根据名称知道该类的职责。是的,我知道命名只是整个轮子的一个辐条,物理和逻辑结构在理解代码方面也起着重要作用,复杂性也是如此。在这篇文章中,我只关注命名,因为我认为它对理解代码的影响最大。
除非类型能澄清意图,否则不要包含类型
类型可以是任何东西,从编程类型(字符串、整数、十进制)到职责分组(Util、Helper、Validator、Event等)。通常它是一种分类,不表达意图。
让我们看一个例子:名称 StringHelper 表达的意思不多。string 是系统类型,Helper 是模糊的,StringHelper 更多地说的是”如何”而不是意图。如果我们将名称改为 DisplayNameFormatter,我们会得到更清晰的意图图景。DisplayName 非常具体,Formatter 表达结果。Formatter 可能是也可能不是一种类型,但这无关紧要,因为它表达了意图。
总有例外;例如,在 ASP.Net MVC 中,控制器必须以”Controller”结尾,否则应用程序无法运行。使用诸如领域驱动设计(DDD)之类的范例,“Services”、“Repository”、“ValueType”和”Model”等名称在 DDD 中有意义并表达职责。
例如,UserRepository 意味着用户数据被检索并保存到数据存储。
避免使用隐喻
隐喻是文化性的,来自其他文化的工程师可能不理解意图。
美国常见的隐喻:
- Beating a dead horse
- Chicken or the egg
- Elephant in the room
新西兰常见的隐喻:
- Spit the dummy
- Knackered
- Hard yakka
使用动词
Steve Yegge 写了一篇(非常长的)博客文章关于使用动词而不是名词。
他的观点是使用动词,应用程序由名词组成,但名词不做任何事情。系统仅有名词是无用的,相反,在方法名称中表达动作。
例如,UserAuthentication*(名词).AuthenticateUser(动作/动词)* 表达验证用户凭证的动作。
要有描述性
要有描述性,细节越多越好——在名称中表达职责。
问自己,这个类或函数做得最好的一件事是什么?
如果你很难找到一个名称,该类或函数可能有多个职责,因此违反了单一职责原则。
不要依赖注释来表达意图
注释是为代码提供额外上下文的好方法,但不要依赖注释。类和方法的名称应该独立存在。
在 Martin Fowler、Kent Beck、John Brant、William Opdyke 和 Don Roberts 的《重构:改进既有代码的设计》中:
……注释经常被用作除臭剂。令人惊讶的是,你经常看到注释很多的代码,并注意到注释之所以存在是因为代码很糟糕。
《重构》作者的另一个精彩引用:
当你感到需要写注释时,首先尝试重构代码,使任何注释变得多余。第88页
很多时候,当代码被重构并封装到一个方法中时,你会发现其他位置也可以利用新方法,这些地方是你在使用新方法时从未预料到的。
有时在调用方法时,使用者需要了解关于该方法的特定信息,如果该特定信息是名称的一部分,那么使用者就不需要查看源代码。
这是一个将注释合并到名称中的例子。
带注释的:
// without tracking
var user = GetUserByUserId(userId);
重构为在方法名称中包含注释:
var userWithOutTracking = GetUserByUserIdWithoutTracking(userId);
其他工程师现在知道这个方法没有跟踪,而不需要阅读源代码或查找注释。
注释应该是你最后的防线,尽可能地依靠其他方式来表达意图,包括使用物理和逻辑结构以及名称来传达意图。
避免使用含义模糊的名称
避免使用含义模糊的名称。模糊名称的含义因项目而异,这使得新工程师更难理解意图。
以下是常见模糊名称的列表:
- Helper
- Input
- Item
- Logic
- Manager
- Minder
- Moniker
- Nanny
- Overseer
- Processor
- Shepherd
- Supervisor
- Thingy
- Utility
- Widget
使用与业务领域相同的语言
在代码中使用与业务领域相同的术语。这允许工程师和主题专家(SME)轻松交流想法,因为他们共享相同的词汇。当没有共享词汇时,翻译就会发生,这必然导致误解。
在我参与的一个项目中,业务部门开始使用”Pupil”,然后切换到”Student”。软件工程师从未更新软件以反映术语的变化。当新工程师加入项目时,大多数人认为 Pupil 和 Student 是不同的概念。
使用行业术语
尽可能使用在整个软件行业中有意义的术语。
大多数软件工程师,当他们看到名为”factory”的东西时,他们立即想到工厂模式。
使用现有的应用范例,如”清洁架构”和”领域驱动设计”,便于想法共享,并为工程师创建了一种共同的语言来相互交流想法。
最糟糕的命名是挪用行业范围内的术语并赋予其不同的含义。
命名布尔值时……
布尔名称应该始终是对问题的回答,其值为真或假。
例如,isUserAuthenticated,答案要么是是 (true) 要么是否 (false)
使用以下单词:
- Has
- Does
- Is
- Can
避免否定名称,例如:
否定的变量名称:
var IsNotDeleted = true; // this is confusing
if(!IsNotDeleted) { // it gets even more confusing when the value is negated
//Do magic
}
没有否定的变量名称:
var IsDeleted = true; // this is confusing
if(!IsDeleted) {
//Do magic
}
总结
选择富有表现力的名称对于向下一位工程师传达意图、设计和领域知识至关重要。通常我们在代码上工作以修复缺陷或合并新功能,我们不断地在脑海中编译代码以试图理解它的工作原理。命名给了我们关于前一位工程师在想什么的提示,没有这种过去和未来工程师之间的沟通,我们会阻碍自己在应用程序增长中的发展。可能会导致项目失败。
作者:Chuck Conway 是一位 AI 工程师,拥有近 30 年的软件工程经验。他构建实用的 AI 系统——内容管道、基础设施代理和解决实际问题的工具——并分享他沿途的学习成果。在社交媒体上与他联系:X (@chuckconway) 或访问他的 YouTube 和 SubStack。