Java接口/实现命名约定

你如何命名你创建的不同的类/接口? 有时候我没有实现信息可以添加到实现名中——比如接口FileHandler和类SqlFileHandler.

当这种情况发生时,我通常以“正常”名称命名接口,如Truck,并将实际类命名为TruckClass

在这方面,如何命名接口和类?

291627 次浏览

标准的c#约定(在Java中也很适用)是用I作为所有接口的前缀——所以你的文件处理程序接口将是IFileHandler,你的卡车接口将是ITruck。它是一致的,并且很容易区分接口和类。

有些人不喜欢这样,它更像是。net的约定而不是Java,但是你可以用大写的I前缀来命名你的接口,例如:

IProductRepository - interface
ProductRepository, SqlProductRepository, etc. - implementations

反对这种命名约定的人可能会争辩说,您不应该关心您在代码中使用的是接口还是对象,但我发现它更容易快速阅读和理解。

我不会用“class”后缀来命名实现类。这可能会导致混乱,因为您实际上可以在代码中使用“类”(即类型)对象,但在您的情况下,您不是在使用类对象,而是在使用普通的旧对象。

命名你的InterfaceTruck。不是ITruck,因为它不是ITruck而是Truck

Java中的Interface类型。然后你有DumpTruckTransferTruckWreckerTruckCementTruckimplements Truck

当你使用Interface来代替子类时,你只需将它强制转换为Truck。如List<Truck>。将I放在前面只是匈牙利的风格符号无谓的重复,它只会在代码中添加更多的东西。

所有现代Java IDE的标记接口和实现等等都离不开这个愚蠢的符号。不要称它为TruckClass,它是无谓的重复,就像IInterface同义一样糟糕。

如果它是一个实现,它就是一个类。该规则的唯一真正例外(总是有例外的)可能是类似AbstractTruck的东西。因为只有子类才会看到这个,而且你不应该强制转换到Abstract类,它确实添加了一些信息,说明类是抽象的,以及应该如何使用它。你仍然可以想出一个比AbstractTruck更好的名字,并使用BaseTruckDefaultTruck来代替,因为abstract在定义中。但是由于Abstract类永远不应该是任何面向公共的接口的一部分,我相信这是一个可以接受的规则例外。使构造函数protected能够很好地跨越这一界限。

Impl后缀也只是更多的噪音。更多的同义反复。任何不是接口的东西都是实现,即使是部分实现的抽象类也是如此。你要在每个的每个名称上都加上那个愚蠢的Impl后缀吗?

Interface是公共方法和属性必须支持什么的契约,它也是类型信息。所有实现Truck的东西都是Truck类型

看看Java标准库本身。你看到IListArrayListImplLinkedListImpl了吗?不,你看到的是ListArrayList,还有LinkedList。这里有一个关于这个确切问题的文章。这些愚蠢的前缀/后缀命名约定也都违反了原则。

此外,如果你发现自己添加了DTOJDOBEAN或其他愚蠢的重复后缀到对象中,那么它们可能属于而不是所有这些后缀。适当打包的名称空间是自文档化的,并减少了这些构思糟糕的专有命名方案中所有无用的冗余信息,大多数地方甚至在内部都没有以一致的方式坚持这些专有命名方案。

如果你能想出的唯一方法就是用Impl来后缀你的Class名字,那么你需要重新考虑是否有一个Interface。因此,当你有一个Interface和一个Implementation,它不是Interface唯一专门化的情况时,在大多数情况下你可能不需要Interface

然而,一般来说,对于可维护性、可测试性和模拟,提供接口是最佳实践。看到这是更多细节的答案

也可以参考Martin Fowler关于InterfaceImplementationPair这个主题的有趣文章

TruckClass听起来像是Truck的一个类,我认为建议的解决方案是添加Impl后缀。在我看来,最好的解决方案是在实现名称中包含一些信息,在那个特定的实现中发生了什么(就像我们有List接口和实现:ArrayListLinkedList),但有时你只有一个实现,并且由于远程使用而不得不有接口(例如),那么(如开头提到的)Impl是解决方案。

接口的名称应该描述接口所代表的抽象概念。任何实现类都应该有某种特定的特征,可以用来给它一个更具体的名称。

如果只有一个实现类,而且你想不出任何使它特定的东西(因为想要将它命名为-Impl),那么看起来根本就没有理由有接口。

我喜欢指示接口描述的契约的接口名称,例如“可比的”或“序列化的”。像“卡车”这样的名词并不能真正描述卡车——卡车的能力是什么?

关于惯例:我曾经工作过的项目中,每个界面都以“I”开头;虽然这与Java惯例有些不同,但它使查找接口变得非常容易。除此之外,“Impl”后缀是一个合理的默认名称。

我倾向于遵循Java Core/Sun建立的伪约定,例如在Collections类中:

  • List -“概念性”对象的接口
  • 接口的具体实现
  • 接口的具体实现
  • AbstractList -抽象的“部分”实现,以协助自定义实现

我曾经在AWT事件/监听器/适配器范例之后做同样的事情来建模我的事件类。

我使用这两种习惯:

如果接口是一个已知模式(例如Service, DAO)的特定实例,那么它可能不需要“I”(例如UserService, AuditService, UserDao),没有“I”都可以正常工作,因为后修正决定了元模式。

但是,如果你有一些一次性或两次的东西(通常是回调模式),那么它有助于将它与类区分开来(例如IAsynchCallbackHandler, IUpdateListener, IComputeDrone)。这些是为内部使用而设计的特殊用途的接口,有时IInterface会引起注意,操作数实际上是一个接口,因此乍一看就很清楚了。

在其他情况下,您可以使用I来避免与其他常见的具体类(issubject, IPrincipal vs Subject或Principal)发生冲突。

我在这里看到的回答表明,如果你只有一个实现,那么你就不需要接口。这违背了依赖注入/控制反转原则(不要给我们打电话,我们会给你打电话!)。

所以,是的,在某些情况下,您希望通过依赖注入接口实现来简化代码并使其易于测试(这也可能是代理的-您的代码不知道!)即使您只有两个实现——一个是用于测试的Mock,另一个被注入到实际的生产代码中——这也不会使接口变得多余。文档记录良好的接口建立了契约,也可以通过严格的模拟实现来维护契约,以进行测试。

事实上,您可以建立测试,让模拟实现最严格的接口契约(为不应该为空的参数抛出异常,等等)并在测试中捕获错误,在生产代码中使用更有效的实现(不检查不应该为空的参数是否为空,因为模拟在测试中抛出异常,并且由于在这些测试后修复了代码,您知道参数不为空)。

依赖注入/IOC对于新手来说很难理解,但是一旦你理解了它的潜力,你就会想要在任何地方都使用它,你会发现自己一直在做接口——即使只有一个(实际的生产)实现。

对于这个实现(您可以推断,并且您是正确的,我认为用于测试的模拟应该称为Mock(InterfaceName)),我更喜欢使用Default(InterfaceName)这个名称。如果出现更具体的实现,则可以适当地命名它。这也避免了我特别不喜欢的Impl后缀(如果它不是抽象类,它当然是“Impl”!)

我也更喜欢“基类(InterfaceName)”而不是“抽象(InterfaceName)”,因为在某些情况下,你希望你的基类以后成为可实例化的,但现在你被“抽象(InterfaceName)”这个名字卡住了,这迫使你重命名类,可能会造成一些小混乱-但如果它总是Base(InterfaceName),删除抽象修饰符不会改变类是什么。