
命名是主观的和情境化的,它是一门艺术,而对于大多数艺术,我们会发现其中的模式。我通过阅读他人的代码学到了很多。在这篇文章中,我整理了9个准则,希望其他人在我阅读他们的代码时能够遵循这些准则。
当软件工程师打开一个类时,她应该能够基于名称了解该类的职责。是的,我知道命名只是轮子的一个辐条,物理和逻辑结构以及复杂性在理解代码方面也起着重要作用。在这篇文章中,我只专注于命名,因为我认为它对理解代码有最重要的影响。
除非能澄清意图,否则不要包含类型
类型可以是从编程类型(string、int、decimal)到职责分组(Util、Helper、Validator、Event等)的任何东西。通常它是一个不表达意图的分类。
让我们看一个例子:名称StringHelper没有表达太多内容。string是一个系统类型,Helper是模糊的,StringHelper更多地说的是”如何”而不是意图。如果我们将名称改为DisplayNameFormatter,我们就能更清楚地了解意图。DisplayName非常具体,Formatter表达结果。Formatter可能是也可能不是一个类型,但这并不重要,因为它表达了意图。
总有例外;例如,在ASP.Net MVC中,控制器必须以”Controller”结尾,否则应用程序无法正常工作。使用领域驱动设计(DDD)等范式时,像”Services”、“Repository”、“ValueType”和”Model”这样的名称在DDD中有意义并表达职责。
例如,UserRespository暗示用户数据被检索并保存到数据存储中。
避免使用隐喻
隐喻是文化性的,来自其他文化的工程师可能不理解其意图。
美国的常见隐喻:
- 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”的东西时,会立即想到工厂模式。
使用现有的应用程序范式,如”整洁架构”和”领域驱动设计”,有助于思想共享,并为工程师之间交流思想创建共同语言。
最糟糕的命名是挪用行业范围的术语并赋予其不同的含义。
命名布尔值时…
布尔名称应该始终是对问题的回答,其值为true或false。
例如,isUserAutheticated,答案要么是是*(true)要么是否(false)*
使用以下词语:
- Has
- Does
- Is
- Can
避免否定名称,例如:
否定变量名:
var IsNotDeleted = true; // 这很令人困惑
if(!IsNotDeleted) { // 当值被否定时变得更加困惑
//Do magic
}
没有否定变量名:
var IsDeleted = true; // 这很令人困惑
if(!IsDeleted) {
//Do magic
}
总结
选择表达性名称对于向下一个工程师传达意图、设计和领域知识至关重要。我们经常在代码上工作以修复缺陷或合并新功能,我们不断在脑海中编译代码,试图理解它是如何工作的。命名给我们提示前一个工程师在想什么,没有这种过去和未来工程师之间的交流,我们在应用程序的成长中会束缚自己。可能会注定项目失败。
作者:Chuck Conway 专注于软件工程和生成式人工智能。在社交媒体上与他联系:X (@chuckconway) 或访问他的 YouTube。