如何命名创建的不同类/接口?有时我没有要添加到实现名称中的实现信息,比如接口FileHandler和类SqlFileHandler。
当这种情况发生时,我通常用"普通"名称命名接口,比如Truck,并将实际类命名为TruckClass。
在这方面,您如何命名接口和类?
- 您不调用接口ITruck和类Truck?
- 坏的约定是坏的,不管是谁提出的,我已经读到愚蠢的impl后缀首先出现在IBMDeveloperWorks的文章中。
把你的名字命名为Interface。Truck。不是ITruck,因为它不是ITruck,而是Truck。
Java中的EDOCX1 3是一种类型。那么你就有DumpTruck、TransferTruck、WreckerTruck、CementTruck等。
当您使用Interface代替子类时,您只需将其强制转换为Truck。如List所述。把I放在前面只是匈牙利风格的符号重言式,它只增加了代码类型的更多内容。
所有现代Java IDE的标记接口和实现,以及什么都没有这个愚蠢的符号。不要称之为TruckClass,这是重言式,和IInterface重言式一样糟糕。
如果它是一个实现,那么它是一个类。这条规则唯一真正的例外,而且总是有例外的,可能是类似于AbstractTruck的东西。因为只有子类才会看到这一点,并且您永远不应该将其强制转换为Abstract类,所以它确实添加了一些信息,说明类是抽象的,以及如何使用它。你仍然可以想出一个比AbstractTruck更好的名字,用BaseTruck或DefaultTruck代替,因为Abstract在定义中。但是,由于Abstract类不应该是任何面向公共接口的一部分,我认为这是一个可以接受的规则例外。让建造师protected跨越这一鸿沟还有很长的路要走。
而且Impl后缀的噪音也比较大。更多重言式。任何不是接口的东西都是实现,甚至是部分实现的抽象类。你打算在每个班级的每个名字上加上这个愚蠢的Impl后缀吗?
Interface是一个公共方法和属性必须支持的契约,也是类型信息。实现Truck的一切都是Truck的一种。
查看Java标准库本身。你看到IList、ArrayListImpl、LinkedListImpl了吗?不,你看到List和ArrayList,还有LinkedList。这是一篇关于这个确切问题的好文章。这些愚蠢的前缀/后缀命名约定都违反了dry原则。
此外,如果您发现自己在对象中添加了DTO、JDO、BEAN或其他愚蠢的重复后缀,那么它们可能属于一个包,而不是所有这些后缀。正确打包的名称空间是自文档化的,并减少了这些设计糟糕的专有命名方案中所有无用的冗余信息,而大多数地方甚至在内部都不以一致的方式遵循这些专有命名方案。
如果你所能想到的使你的Class的名字唯一的方法就是用Impl给它加上后缀,那么你需要重新考虑有一个Interface。所以,当你有一个Interface和一个Implementation,不是Interface特有的情况下,你可能不需要Interface。
- 如果你有一个不同意这个答案的答案,请把它贴出来。这些评论已经演变成了广泛的讨论,这有点超出了他们的控制范围。
- 关于单个实现的最后一条评论是错误的。接口仍然是一个很好的选择,可能是与同事分离工作,甚至是为JUnit测试创建一个虚拟的或假的实现。
- 对的!还有一件事,由于Abstract*类型没有添加新的合同,它们也应该被认为是错误的。拥有抽象类就是违反SRP。
- 关于dto,我不同意,想象一下"pet"(实体)和"pedto",好吧,这是不雅的,但您可以很容易理解代码。如果是不同包装的宠物和宠物,就不会再口齿不清了。此外,您的示例对于接口也很好,但并不总是那么明显,假设userservice和defaultuserservice,我使用这个解决方案,但对于C stackoverflow.com/questions/681700/interface naming conventi‌&8203;on中真正常见的单个实现,它是否比iService和service更易于理解?
我在这里看到的答案表明,如果您只有一个实现,那么就不需要接口。这违背了依赖注入/控制反转的原则(不要叫我们,我们会叫你!).
所以是的,在某些情况下,您希望简化代码,并通过依赖注入的接口实现(也可能是代理的——您的代码不知道!)即使只有两种实现——一种是模拟测试,另一种是注入到实际的生产代码中——这并不意味着拥有一个多余的接口。一个有良好文档记录的接口建立了一个契约,它也可以通过一个严格的测试模拟实现来维护。
实际上,您可以建立一些测试,让mock实现最严格的接口约定(为不应为空的参数抛出异常等),并在测试中捕获错误,在生产代码中使用更有效的实现(不检查不应为空的参数,因为mock在您的测试中抛出了异常)。例如,STS和您知道参数不是空的,因为在这些测试之后修改了代码)。
依赖注入/IOC对于新手来说可能很难把握,但是一旦你了解了它的潜力,你就会想在整个地方使用它,并且你会发现自己一直在制作接口——即使只有一个(实际生产)实现。
对于这一个实现(您可以推断,而且您是正确的,我认为用于测试的模拟应该称为mock(interfacename)),我更喜欢名称default(interfacename)。如果出现更具体的实现,则可以对其进行适当的命名。这也避免了我特别不喜欢的impl后缀(如果它不是抽象类,当然它是一个"impl"!).
我也更喜欢"base(interfacename)"而不是"abstract(interfacename)",因为在某些情况下,您希望您的基类稍后变得可实例化,但现在您仍然使用"abstract(interfacename)"这个名称,这将强制您重命名该类,可能会导致一点小混乱,但如果它始终是base(interfacename),移除抽象修饰符不会改变类的内容。
- 控制反转或依赖注入的概念不需要为类的单一实现提供接口,一些流行的IOC容器实现迫使您对单个实现的接口做一些愚蠢的事情。简单地将对象传递给构造函数是原始的"依赖注入"。至于模拟,使用类似mockito的东西,让它基于实际的类进行模拟。
- 是的,然后当你在实际的类中有一个bug时,你就不能将这个bug与实际的类隔离开来。毕竟,类的使用是"测试"的。但事实并非如此。您不能通过使用实际的代码来实现隔离。当两个测试失败时——一个用于类,另一个用于使用类的类,哪个是错误的?通过接口隔离只会导致一个测试失败——实际类的测试,而不是使用类实现的接口的类的测试。
- 你不明白Mockito在"实际课堂"上做了什么。
- 您不知道共享模拟实现如何轻松地共享以供测试重用,而不是使用不同测试的模拟框架来定制模拟,而是每次通过设置代码重新发明控制盘。从长远来看,定制模拟实现要干净得多,并且有能够被记录的好处——与动态生成的代理不同。此外,除非您模拟了实现类中的每个方法,否则无法保证被测试单元不依赖于实际的实现——不管怎样,它都应该能够工作。
- @Metroidfan2002我真的很喜欢"默认"。这真的说明了它是什么。接口的"默认"实现。
- 这是我对公认答案的主要反对意见。声明一个接口是绝对有效的,即使您当前只有一个具体化。通过不声明接口,所有的使用者都声明API契约,一旦您决定要添加或迁移到新的实现,这些契约将被废弃。
- 我承认…如果您的API都是内部的,并且您以后可以很容易地重构以注入接口,那么我将完全做到这一点。对我来说,界面在技术上更干净,但是付出了额外的努力(对于作者和读者)。
- 完全正确。接口的另一个用法是去耦。我不敢相信有人告诉你,如果只有一个实现,就不需要接口:/
接口的名称应该描述接口所代表的抽象概念。任何实现类都应该具有某种特定的特性,可以用来给它一个更具体的名称。
如果只有一个实现类,并且您不能想到任何使它具体化的东西(通过想将其命名为-Impl),那么看起来根本没有理由拥有一个接口。
- 我见过许多具有一个接口和一个类来实现该接口的系统,比如IXXX和XXX,或者XXX和XXXIMPL。我不明白这一点。如果您只有一个实现,为什么还要将它分为两种类型呢?你所得到的只是把所有的声明写两次并保持同步。
- @杰伊:这类事情通常是由那些非常正统的人做的,他们对单元测试所有东西都是孤立的,并且不了解(或不了解)允许模拟具体类的模拟框架。
- @ Borgwardt:我认为这是由老C程序员完成的,他们认为,就像在C中,他们必须为每个C模块创建一个.h,认为在Java中,他们必须为每个类创建一个接口!-)
- 将类更改为接口会破坏二进制兼容性。如果一个接口只有一个实现,但是将来您可能有两个实现(向后兼容性很重要),那么仍然应该将协定提取到接口类型中。
- @Nathan:很少有项目需要二进制向后兼容——而且这些项目应该有非常清晰的定义和分离的公共API。
- 我正在创建一个插件系统(类似于Eclipse),它从我的框架的实际源代码中删除插件编写器。我找不到为我的(通常是单一的)私有类编写公共接口的工作。用户想要访问一个笔记本对象,但我不想让他们看到内部,所以我创建了一个i notebook,在不显示代码的情况下为他们提供方法签名。这就是您所说的单独的公共API吗?
- @sdasdas:部分原因是,这意味着您可以决定笔记本类的哪些公共方法是API的一部分,哪些不是API的一部分,而这并不是您应该轻而易举地做出的决定。而且,这些接口可能在不同的包中。
- 在您希望提取一个目前只有一个实现的接口的情况下,我希望尽可能简单地命名该接口,然后在实现类前面加上"default"。因此,在@sdasdas notebook案例中,接口将是notebook,实现将是defaultnotebook。我认为这符合CoC的概念,但是如果需要的话,可以在框架中或者由依赖于框架的应用程序重写(双关语)笔记本实现。
- @Martinwoolstenhulme这是一个很好的观点,我相信Swing库遵循大多数模型的标准;例如,DefaultTableModel。
- 总之:defaultnotebook或notebook impl用于与单个实现的接口。
我倾向于遵循JavaCys/Sun建立的伪约定,例如在集合类中:
- List"概念"对象的接口
- ArrayList—接口的具体实现
- LinkedList—接口的具体实现
- AbstractList—抽象的"部分"实现以帮助自定义实现
小精灵
我曾经在awt事件/侦听器/适配器范式之后对我的事件类进行建模。
在Java中工作得很好的标准C.C约定是用EDCOX1(0)}对所有接口进行前缀-因此,您的文件处理程序接口将是EDCOX1×1,而您的卡车接口将是EDCOX1×2。它是一致的,并且使区分接口和类变得容易。
- 这只是COM编程的一个倒退。为什么要区分接口和类?
- 帮助它在您查看类名时脱颖而出,并且没有为您指出它的IDE。接口是重要的,应适当设计。
- 我曾经和C语言一起工作,现在用Java工作。我觉得C约定更合乎逻辑。这个长线程向您展示了对接口和植入的名称约定的需要,所以说不需要这样做有点屈尊俯就。
- 的定义告诉您它是什么public interface Truck非常明确。
- 如果您正在查看接口代码本身,那么它是显式的。如果您正在搜索一个接口的整个类,或者正在查看其他人的代码,那么让这些接口单独可见真的很有帮助。
- @Jarrodroberson:我倾向于同意Jamengulfer——在进行代码审查时,您可能没有IDE突出显示的帮助。如果有一个明确的约定,并且单字类型(truck)是接口,而多字类型(simpletruck)是类,那么有这个约定是有意义的。
- @Galmorad——逻辑谬误对传统的吸引力/对共同实践的吸引力
我喜欢指示接口描述的契约的接口名称,例如"可比较"或"可序列化"。像"卡车"这样的名词并不能真正描述卡车的特性——卡车有什么能力?
关于约定:我已经研究了每个接口从一个"i"开始的项目;虽然这与Java约定有点陌生,但它使得查找接口非常容易。除此之外,"impl"后缀是一个合理的默认名称。
- 江户十一〔一〕号?…
- 如果模式成立(Comparable、Serializable…),则应为Truckable。
- 在现实世界中,卡车可能会扩展车辆接口,从而提供一些更常见的行为和属性。
- @罗伯特·哈维,@bert f:不,问题是,卡车做什么?他们是在你的特定代码中做的吗?例如,它们可能是"可修复的"。
- @麦基-这是个玩笑-我相信罗伯特的评论也是。
- @我相信麦基的评论也是个笑话。不管怎样,它应该是卡车意识。
- 事实上最好的答案!
- 我最喜欢这个答案——一个接口应该指定某种功能,而Truck并没有真正描述接口的功能。Sortable、Comparable、Disposable等更具体地描述了功能,由于有许多更小的接口比较好,这很适合这种使用模式。
- 不要对接口应该或不应该指定什么做太多的假设;这取决于具体情况。您所能说的是,接口是底层实现的(部分)抽象,在某些上下文中是有用的。因此,根据手头的问题,序列化、TruckloadAware和Truck都可以是有效的接口名称。
- 这没有道理,那么你认为应该是Listable的List是什么?这太荒谬了。
- 一个名为EDCOX1(0)的接口从Java API的接口EDOCX1到4是有意义的。
- 这只是一个逻辑谬论,它呼吁传统/呼吁共同实践。
- @贾罗德罗伯森-嗯…这是不可能的,对吧?我认为问题是这种方法只适用于具有单一行为的接口。更复杂的接口实际上是复合概念,定义了一组相关行为,例如列表组合了可添加、可移动、可索引等。
- 这个答案,特别是关于"指示接口描述的契约"的部分,被严重低估了。通过这种方式实现接口,可以避免各种各样的问题。
有些人不喜欢这个,它更多的是.NET约定,而不是Java,但是你可以用一个Capital I前缀来命名你的接口,例如:
1 2
| IProductRepository - interface
ProductRepository, SqlProductRepository, etc. - implementations |
反对这种命名约定的人可能会争辩说,您不应该关心您在代码中使用的是接口还是对象,但我发现在运行中更容易阅读和理解。
我不会用"class"后缀命名实现类。这可能会导致混淆,因为您实际上可以在代码中使用"class"(即type)对象,但在您的情况下,您不使用class对象,而是使用一个普通的旧对象。
- 我们不需要Java中的匈牙利符号
- 没有什么"在飞行中更容易阅读和理解"……如果你在代码"truck.drive()"中看到,为什么你会关心卡车的类型是truck还是itruck?知道卡车是接口的一个实现,您会得到什么有用的信息?没有什么。如果有一种卡车类型,你需要专门化它,那么你就去找出什么是卡车。
- @斯蒂夫阔·姆菲尔德比同工同酬更愚蠢。
- 这只是一个逻辑谬论,它呼吁传统/呼吁共同实践。
我使用两种约定:
如果接口是一个已知模式(如服务、DAO)的特定实例,那么它可能不需要一个"i"(如用户服务、审计服务、用户DAO),没有"i"都可以正常工作,因为后缀决定了元模式。
但是,如果您有一个或两个关闭(通常用于回调模式),那么它有助于将其与类(例如,iasynchcallbackhandler、iupdatelistener、icomputedrone)区分开来。这些是专门为内部使用而设计的接口,偶尔iInterface会引起人们对操作数实际上是接口这一事实的注意,因此乍一看就很清楚。
在其他情况下,可以使用i来避免与其他常见的具体类(i subject、i principal、subject或principal)发生冲突。
- 甚至不总是做得不好:(-)
- 那些"服务"和"DAO"后缀应该是包,那么您也不需要那些无用的后缀。
- 我真的不觉得包鉴定在可读性方面澄清了任何东西(尽管从技术上讲它消除了歧义)。考虑一下Hibernate的会话,它与外面的其他会话冲突。任何同时使用这两种代码的代码都是一团混乱,需要仔细考虑和阅读。
- 使用包名"dao"是很愚蠢的。这意味着这里只有DAO类,但实际上我可能也希望我的域对象在那里。为什么不把你的卡车、皮卡卡车和卡车道放在你的"卡车"包裹里?事实上,如果你连接卡车道,这意味着你以后可以通过不同的方式拉卡车。不知道你为什么要具体分类。
TruckClass听起来像是Truck的一类,我认为建议的解决方案是添加Impl后缀。在我看来,最好的解决方案是在实现名称中包含一些信息,在特定的实现中发生了什么(就像我们在List接口和实现中使用的那样:ArrayList或LinkedList),但有时您只有一个实现,并且由于远程使用(例如),必须具有接口。en(如开头所述)是解决方案。
- 你打败了我。但是,IMPL后缀是一个很好的选择。
- 我同意,把"卡车"作为接口和"TunkIMPL",因为类名似乎是Java中最常用的方法。
- 类不需要后缀来显示它们是接口的实现;这就是implements关键字的作用。如果我在所有的类名后面都看到了Impl,我想我会开枪自杀。
- 普通并不意味着正确,罗伯特·哈维是100%正确的。
- @罗伯特·哈维那么,如果我只有一个实现的接口,并且没有具体的信息可以包含在它的名称中,我该怎么做呢?
- 嗯,我在营地里说区分你的接口名,而不是类名。如果你的Truck类实现了Vehicle接口怎么办?那么命名你的类TruckImpl就没有任何意义了。但是,您可以将接口命名为VehicleInterface或IVehicle(甚至,如果这是您的敏感度,也可以简单地命名为Vehicle),并且可以毫无问题地调用类Truck。
- @罗伯特·哈维,如果接口代表一辆通用汽车,而卡车是一辆汽车的"专业化",那就好了。但它恰好是卡车公司软件的一个例子。所以在使用接口时应该说truck。否则就很尴尬了。
- 如果您有一个实现和一个接口,那么您可能不应该首先有一个接口。
- 这只是一个逻辑谬论,它呼吁传统/呼吁共同实践。
- @Jarrodroberson-你是说卡车接口是可分类的吗?D
- @罗伯特哈维-你担心射错人。-)