FileHandler
SqlFileHandler
当这种情况发生时,我通常以“正常”名称命名接口,如Truck,并将实际类命名为TruckClass。
Truck
TruckClass
在这方面,如何命名接口和类?
标准的c#约定(在Java中也很适用)是用I作为所有接口的前缀——所以你的文件处理程序接口将是IFileHandler,你的卡车接口将是ITruck。它是一致的,并且很容易区分接口和类。
I
IFileHandler
ITruck
有些人不喜欢这样,它更像是。net的约定而不是Java,但是你可以用大写的I前缀来命名你的接口,例如:
IProductRepository - interface ProductRepository, SqlProductRepository, etc. - implementations
反对这种命名约定的人可能会争辩说,您不应该关心您在代码中使用的是接口还是对象,但我发现它更容易快速阅读和理解。
我不会用“class”后缀来命名实现类。这可能会导致混乱,因为您实际上可以在代码中使用“类”(即类型)对象,但在您的情况下,您不是在使用类对象,而是在使用普通的旧对象。
命名你的Interface。Truck。不是ITruck,因为它不是ITruck而是Truck。
Interface
Java中的Interface是类型。然后你有DumpTruck, TransferTruck, WreckerTruck, CementTruck等implements Truck。
DumpTruck
TransferTruck
WreckerTruck
CementTruck
implements Truck
当你使用Interface来代替子类时,你只需将它强制转换为Truck。如List<Truck>。将I放在前面只是匈牙利的风格符号无谓的重复,它只会在代码中添加更多的东西。
List<Truck>
所有现代Java IDE的标记接口和实现等等都离不开这个愚蠢的符号。不要称它为TruckClass,它是无谓的重复,就像IInterface同义一样糟糕。
IInterface
如果它是一个实现,它就是一个类。该规则的唯一真正例外(总是有例外的)可能是类似AbstractTruck的东西。因为只有子类才会看到这个,而且你不应该强制转换到Abstract类,它确实添加了一些信息,说明类是抽象的,以及应该如何使用它。你仍然可以想出一个比AbstractTruck更好的名字,并使用BaseTruck或DefaultTruck来代替,因为abstract在定义中。但是由于Abstract类永远不应该是任何面向公共的接口的一部分,我相信这是一个可以接受的规则例外。使构造函数protected能够很好地跨越这一界限。
AbstractTruck
Abstract
BaseTruck
DefaultTruck
abstract
protected
而Impl后缀也只是更多的噪音。更多的同义反复。任何不是接口的东西都是实现,即使是部分实现的抽象类也是如此。你要在每个类的每个名称上都加上那个愚蠢的Impl后缀吗?
Impl
Interface是公共方法和属性必须支持什么的契约,它也是类型信息。所有实现Truck的东西都是Truck的类型。
看看Java标准库本身。你看到IList, ArrayListImpl, LinkedListImpl了吗?不,你看到的是List和ArrayList,还有LinkedList。这里有一个关于这个确切问题的文章。这些愚蠢的前缀/后缀命名约定也都违反了干原则。
IList
ArrayListImpl
LinkedListImpl
List
ArrayList
LinkedList
此外,如果你发现自己添加了DTO, JDO, BEAN或其他愚蠢的重复后缀到对象中,那么它们可能属于包而不是所有这些后缀。适当打包的名称空间是自文档化的,并减少了这些构思糟糕的专有命名方案中所有无用的冗余信息,大多数地方甚至在内部都没有以一致的方式坚持这些专有命名方案。
DTO
JDO
BEAN
如果你能想出的唯一方法就是用Impl来后缀你的Class名字,那么你需要重新考虑是否有一个Interface。因此,当你有一个Interface和一个Implementation,它不是Interface唯一专门化的情况时,在大多数情况下你可能不需要Interface。
Class
Implementation
然而,一般来说,对于可维护性、可测试性和模拟,提供接口是最佳实践。看到这是更多细节的答案。
也可以参考Martin Fowler关于InterfaceImplementationPair这个主题的有趣文章
TruckClass听起来像是Truck的一个类,我认为建议的解决方案是添加Impl后缀。在我看来,最好的解决方案是在实现名称中包含一些信息,在那个特定的实现中发生了什么(就像我们有List接口和实现:ArrayList或LinkedList),但有时你只有一个实现,并且由于远程使用而不得不有接口(例如),那么(如开头提到的)Impl是解决方案。
接口的名称应该描述接口所代表的抽象概念。任何实现类都应该有某种特定的特征,可以用来给它一个更具体的名称。
如果只有一个实现类,而且你想不出任何使它特定的东西(因为想要将它命名为-Impl),那么看起来根本就没有理由有接口。
-Impl
我喜欢指示接口描述的契约的接口名称,例如“可比的”或“序列化的”。像“卡车”这样的名词并不能真正描述卡车——卡车的能力是什么?
关于惯例:我曾经工作过的项目中,每个界面都以“I”开头;虽然这与Java惯例有些不同,但它使查找接口变得非常容易。除此之外,“Impl”后缀是一个合理的默认名称。
我倾向于遵循Java Core/Sun建立的伪约定,例如在Collections类中:
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),删除抽象修饰符不会改变类是什么。