引言
当我在设计软件时,我会花很多时间去考虑命名。对我而言,命名是设计过程中重要的一部分。命名就是在定义。
In the beginning was the Word, and the Word was with God, and the Word was God.
—— 约翰福音
我知道设计到位的一种感受,就是命名正确了。这可能需要一些时间(我在刚开始命名时往往会反复),但没关系,良好的设计需要付出。
当然,好的命名并不代表好的设计,但是糟糕的命名绝对会毁了一个好的设计。基于此,下面我分享了我在命名时的一些准则。虽然示例是C++的,但是对面向对象的语言或多或少有点参考价值。
类型(class,interface,struct)
名称应该是一个名词短语。
1 | Bad: Happy |
不要使用命名空间的前缀,因为这是命名空间的作用。
1 | Bad: SystemOnlineMessage |
使用恰如其分的形容词来明确含义。
1 | Bad: IAbstractFactoryPatternBase |
不要在类型名称中使用”Manger”、”Helper”这种无意义的单词。如果一种类型有这种词,要么是命名失败要么是设计失败。类型应该是不言自明的。
1 | Bad: ConnectionManager |
如果一个类不代表具体的东西,考虑使用隐喻。
1 | Bad: IncomingMessageQueue |
如果使用隐喻,请一以贯之。
1 | Bad: Mailbox, DestinationID |
函数(function,procedure)
简单明了。
1 | Bad: list.GetNumberOfItems(); |
别过于简洁。
1 | Bad: list.Verify(); |
缩写正确。
1 | Bad: list.Srt(); |
用动词命名执行操作的函数。
1 | Bad: obj.RefCount(); |
像问问题一样命名返回布尔值的函数。
1 | Bad: list.Empty(); |
用名词命名只返回值不改变状态的函数。
1 | Bad: list.GetCount(); |
不要让名称与参数重复。
1 | Bad: list.AddItem(item); |
不要让名称和执行的对象重复。
Don’t make the name redundant with the receiver.
1 | Bad: list.AddToList(item); |
只有在返回值类型有多种重载时才强调它。
1 | Bad: list.GetCountInt(); |
不要在命名里使用“and”和“or”。如果一个名称中有连词,该函数可能承担了过多的职责,不妨将其拆解开来并分别命名。考虑给一个函数或类进行命名,可以帮你思考这是否是一个原则操作。
1 | Bad: mail.VerifyAddressAndSendStatus(); |
这重要吗?
对我来说,这个问题的答案显而易见。一个命名良好的模块很快就能告诉你它的作用,仅仅阅读一小部分,你就能快速构建起对整个系统的认知模型。如果系统中有“MailBox”,即使你不读代码,你也会期望看到“Mail”、“Addresses”这种概念。
命名良好的代码可以帮助你更好地和其他同事沟通,这有助于代码知识的传播。没有人想在沟通的时候一直重复类似 “ISrvMgrInstanceDescriptorFactory” 这种名字。
另一方面,糟糕的命名就像给你加了一堵空气墙,强迫你努力在脑海里运行这些代码,思考它的目的,然后转化到你自己的话语体系下。“哦,DoCheck()看起来是在遍历检查connection是否都live着,那我不如叫它AreConnectionsLive()”,整个过程缓慢并且没办法共享。
从我看过的代码来看,模块内命名的一致性和模块的一致性往往是相辅相成的。当我感觉命名很难时,很有可能是这个东西本身设计就有问题,可能是违背了单一职责原则,或者是缺少了某些部分。
很难说我们的设计好或者坏,但是我很确信的一点就是当命名困难时,设计往往是错误的。现在我在设计时会尝试关注这一点,一旦我觉得命名不错,通常来说我对设计也比较满意。