命名类 – 如何避免把一切都称为“<WhatEver>pipe理器”?
很久以前,我已经阅读了一篇文章(我相信一个博客文章),这个文章将我放在命名对象的“正确”轨道上:在程序中对命名做出非常谨慎的规定。
例如,如果我的应用程序(作为一个典型的商业应用程序)处理用户,公司和地址,我会有一个User
,一个Company
和一个Address
域类 – 可能在某处UserManager
,一个AddressManager
和一个AddressManager
会popup处理那些事。
那么你能告诉那些UserManager
, AddressManager
和AddressManager
做什么吗? 不,因为经理是一个非常通用的术语,适合任何你可以用你的域对象做的事情。
我读的文章推荐使用非常具体的名字。 如果这是一个C ++应用程序,并且UserManager
的工作是从堆中分配和释放用户,那么它不会pipe理用户,而是保护他们的出生和死亡。 嗯,也许我们可以称之为UserShepherd
。
或者UserManager
的工作是检查每个用户对象的数据并以密码方式签署数据。 然后我们有一个UserRecordsClerk
。
现在,这个想法坚持我,我尝试应用它。 而且很难find这个简单的想法。
我可以描述这些课程所做的事情(只要我没有陷入快速和肮脏的编码中),我写的课程只能做一件事。 我想从这个描述到名字的想法是一种名称目录,一个将概念映射到名称的词汇表。
最终,我想在我的脑海里有一个类似于模式目录的东西(通常devise模式很容易提供对象名称,例如工厂 )
- 工厂 – 创build其他对象(从devise模式中获取的命名)
- 牧羊人 – 一个牧羊人处理对象的生命周期,他们的创build和closures
- 同步器 – 在两个或多个对象(或对象层次结构)之间复制数据
-
保姆 – 帮助对象在创build后达到“可用”状态 – 例如通过连接到其他对象
-
等等
那么,你如何处理这个问题呢? 你是否有固定的词汇量,你是否即刻发明新的名字,或者你认为命名不重要或错误?
PS:我也感兴趣的文章和博客讨论这个问题的链接。 首先,这是原来的文章,让我想起它: 命名Java类没有“经理”
更新:答案摘要
下面是我从这个问题中学到的东西的一个小结。
- 尽量不要创造新的隐喻(保姆)
- 看看其他的框架是做什么的
关于此主题的更多文章/书籍:
- 你有什么名字定期地join/附加到课堂上?
- 什么是命名类的最好方法?
- 书: devise模式:可重用面向对象软件的元素(精装)
- 图书: 企业应用架构模式(精装)
- 图书: 实施模式(平装)
还有我从答案中收集到的名字前缀/后缀的列表(主观上!):
- 协调员
- 生成器
- 作家
- 读者
- 处理器
- 容器
- 协议
- 目标
- 变stream器
- 调节器
- 视图
- 厂
- 实体
- 桶
和一个很好的小路:
不要命名瘫痪。 是的,名字是非常重要的,但他们不重要,浪费了大量的时间。 如果你在10分钟内想不出一个好名字,继续前进。
我问了一个类似的问题 ,但在可能的情况下,我尝试复制已经在.NET框架中的名称,并且在Java和Android框架中寻找想法。
看起来, Helper
, Manager
和Util
是你不可避免的名词,你协调不包含国家,通常是程序性和静态的类。 Coordinator
是另一种select。
你可能会得到一些特别的紫色,并且会像Minder
, Overseer
, Supervisor
, Administrator
和Master
,但正如我所说的,我更喜欢把它保留为你所使用的框架名称。
在.NET框架中还有其他一些常见的后缀(如果这是正确的术语):
-
Builder
-
Writer
-
Reader
-
Handler
-
Container
你可以看一看source-code-wordle.de ,我已经分析了.NET框架和其他一些库的最常用的类名后缀。
前20名是:
- 属性
- types
- 帮手
- 采集
- 变stream器
- 处理器
- 信息
- 提供商
- 例外
- 服务
- 元件
- 经理
- 节点
- 选项
- 厂
- 上下文
- 项目
- devise师
- 基础
- 编辑
我都是名副其实的,而且我经常写关于select名字时要格外小心的重要性。 出于同样的原因,在命名事物时,我比较担心隐喻。 在原来的问题中,“工厂”和“同步器”看起来像他们似乎意义的好名字。 但是,“牧羊人”和“保姆”不是,因为他们是基于隐喻的 。 你的代码中的一个类不能真的是一个保姆; 你把它叫做保姆,因为它看起来像一个真正的保姆照顾婴儿或孩子一些其他的东西。 在非正式的演讲中,这是可以的,但是在我看来,对于代码中的类名的确定并不确定,谁知道什么时候知道谁。
为什么? 因为隐喻是依赖于文化的,而且通常也是个体依赖的。 对你来说,命名一个class级的“保姆”可以很清楚,但也许对其他人来说不是很清楚。 除非你正在编写只供个人使用的代码,否则我们不应该依赖它。
在任何情况下,惯例都可以制造或打破一个隐喻。 “工厂”本身的使用是以隐喻为基础的,但是这个隐喻已经存在了很长一段时间,目前在编程界相当知名,所以我认为它是安全的。 然而,“保姆”和“牧羊人”是不可接受的。
如果我们正确地模拟现实世界,我们可以没有任何xxxFactory
, xxxManager
或xxxRepository
类:
Universe.Instance.Galaxies["Milky Way"].SolarSystems["Sol"] .Planets["Earth"].Inhabitants.OfType<Human>().WorkingFor["Initech, USA"] .OfType<User>().CreateNew("John Doe");
😉
这听起来像是在dailywtf.com上发表的东西,“人人有经理的经理”等。
我认为这是一个单一的pipe理类是不正确的devise,但使用“经理”是不错的。 我们可以将其分解为UserAccountManager,UserProfileManager,UserSecurityManager等,而不是UserManager。
“经理”是一个好词,因为它清楚地表明,一个class级并不代performance实世界的“事物”。 'AccountsClerk' – 我该如何判断这是一个pipe理用户数据的类,还是代表他们工作的账户pipe理员?
既然你对这个领域的文章感兴趣,你可能会对Steve Yegge的意见文章“名词王国的执行”感兴趣:
http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html
当我发现自己想要在一个类名中使用Manager
或Helper
时,我认为它是一种代码异味,意味着我还没有find正确的抽象,并且/或者我违反了单一责任原则 ,所以重构和付出更多的努力进入devise往往使命名更容易。
但是,即使devise良好的类也不(总是)自己命名,你的select部分取决于你是在创build商业模型类还是技术基础类。
商业模式类可能很难,因为它们对于每个领域都是不同的。 有一些术语我使用了很多,比如策略类(例如LateRentalPolicy
),但是这些LateRentalPolicy
通常是从创build一个“ 无处不在的语言 ”开始的,这些语言可以与业务用户分享,devise和命名类,模拟真实世界的想法,对象,行为和事件。
技术基础设施类比较容易一些,因为它们描述了我们所熟知的领域。 我更喜欢将devise模式名称合并到类名称中,例如InsertUserCommand,
CustomerRepository,
或SapAdapter.
我理解关于实现而不是意图沟通的担心,但是devise模式与类devise的这两个方面结合在一起 – 至less在处理基础结构时,即使隐藏细节,实现devise也是透明的。
与(如) GOF书所定义的模式一样,命名对象使得我在命名类,组织它们和传达意图方面有很长的路要走。 大多数人会理解这个术语(或者至less是它的一个主要部分)。
如果我不能为我的class级提供一个比XyzManager更具体的名字,那么我将重新考虑这个function是否属于一个class级,即build筑的“代码味道”。
具体到C#,我发现“框架devise指南:可重用.NET库的约定,习惯和模式”有很多关于命名逻辑的良好信息。
至于find那些更具体的词,我经常用一个词库,跳过相关的词来尝试find一个好的词。 尽pipe我尽量不花费太多时间,但是随着我在开发过程中取得了更好的名称,或者有时候认识到SuchAndSuchManager
应该被分解成多个类,然后这个被弃用的类的名字变成了一个非SuchAndSuchManager
的名字,问题。
我认为最重要的是要记住:名称是否足够描述? 你可以通过查看class级应该做的事情来看出这个名字吗? 在你的类名中使用诸如“经理”,“服务”或“处理程序”这样的词语可能被认为是过于通用的,但是由于很多程序员使用了它们,这也有助于理解这个类是什么。
我自己一直在使用门面模式(至less,我认为这就是所谓的)。 我可以有一个只描述一个用户的Users
类和一个跟踪我的“用户集合”的Users
类。 我不把这个类叫做UserManager
因为我不喜欢现实生活中的pipe理者,也不想让他们想起他们:)简单地使用复数forms,可以帮助我理解class级的作用。
我相信这里的关键是要在代码的可见性范围内保持一致,即只要每个需要查看/处理代码的人都理解你的命名约定,那么即使你决定调用它们,也应该没问题'CompanyThingamabob'和'UserDoohickey'。 如果你在一家公司工作,第一站就是看看是否有公司的命名规则。 如果没有或者你没有为一家公司工作,那么使用对你有意义的条款来创build你自己的条款,把它传递给一些至less可以随便编码的值得信任的同事/朋友,并且合并任何有意义的反馈。
应用别人的惯例,即使被广泛接受,如果它不能跳出你的页面,在我的书中是一个错误。 首先,我需要了解我的代码,而不参考其他文档,但同时它需要足够通用,对于同一行业的同一领域的其他人来说也不是难以理解的。
我会考虑你的系统使用的模式,命名约定/编目/类的分组倾向于由所使用的模式定义。 就我个人而言,我坚持这些命名约定,因为它们是另一个人能够拿起我的代码并运行的最可能的方式。
例如,UserRecordsClerk可能更好地解释为扩展了一个通用的RecordsClerk接口,UserRecordsClerk和CompanyRecordsClerk实现并专注于接口,这意味着可以查看接口中的方法来查看其子类通常所用的方法。
查看书籍,如devise模式的信息,这是一本很好的书,可以帮助你清理你的目标是在你的代码 – 如果你还没有使用它! ; O)
我认为只要你的模式select得当,并尽可能使用,那么非常不直观的类名就够了!