你如何命名你创建的不同的类/接口? 有时我没有实现信息可以添加到实现名称中——比如接口FileHandler和类SqlFileHandler。
当这种情况发生时,我通常将接口命名为“正常”名称,如Truck,并将实际的类命名为TruckClass。
在这方面,如何命名接口和类?
你如何命名你创建的不同的类/接口? 有时我没有实现信息可以添加到实现名称中——比如接口FileHandler和类SqlFileHandler。
当这种情况发生时,我通常将接口命名为“正常”名称,如Truck,并将实际的类命名为TruckClass。
在这方面,如何命名接口和类?
标准的c#约定(在Java中也很好用)是给所有接口加上一个I前缀,所以你的文件处理程序接口将是IFileHandler,你的卡车接口将是ITruck。它是一致的,并且很容易区分接口和类。
有些人不喜欢这样,它更像是。net的约定而不是Java,但是你可以用大写的I前缀来命名你的接口,例如:
IProductRepository - interface
ProductRepository, SqlProductRepository, etc. - implementations
反对这种命名约定的人可能会争辩说,您不应该关心您在代码中使用的是接口还是对象,但我发现它更容易快速阅读和理解。
我不会用“class”后缀来命名实现类。这可能会导致混乱,因为您实际上可以在代码中使用“类”(即类型)对象,但在您的情况下,您不是在使用类对象,而是在使用普通的旧对象。
命名你的接口。卡车。不是卡车,因为它不是卡车,它是卡车。
Java中的接口是一种类型。然后你有DumpTruck, TransferTruck, WreckerTruck, CementTruck等实现了Truck。
当您使用接口代替子类时,只需将其转换为Truck即可。如List<Truck>。把I放在前面只是匈牙利风格的符号重复,它只会在代码中添加更多的东西。
所有现代Java IDE的标记接口和实现等等都离不开这个愚蠢的符号。不要叫它TruckClass,那是和IInterface一样糟糕的重述。
If it is an implementation it is a class. The only real exception to this rule, and there are always exceptions, could be something like AbstractTruck. Since only the sub-classes will ever see this and you should never cast to an Abstract class it does add some information that the class is abstract and to how it should be used. You could still come up with a better name than AbstractTruck and use BaseTruck or DefaultTruck instead since the abstract is in the definition. But since Abstract classes should never be part of any public facing interface I believe it is an acceptable exception to the rule. Making the constructors protected goes a long way to crossing this divide.
Impl后缀也是杂音。更多的同义反复。任何不是接口的东西都是实现,即使是部分实现的抽象类也是如此。你打算在每个类的每个名字上都加上那个愚蠢的Impl后缀吗?
接口是公共方法和属性必须支持的内容的契约,它也是类型信息。所有实现卡车的东西都是一种卡车类型。
看看Java标准库本身。你看到IList ArrayListImpl LinkedListImpl了吗?不,你看到的是列表和数组列表,还有LinkedList。这里有一篇关于这个问题的好文章。任何这些愚蠢的前缀/后缀命名约定都违反了DRY原则。
此外,如果您发现自己向对象添加了DTO、JDO、BEAN或其他愚蠢的重复后缀,那么它们可能属于一个包,而不是所有这些后缀。适当打包的名称空间是自文档化的,并减少了这些构思糟糕的专有命名方案中所有无用的冗余信息,大多数地方甚至在内部都没有以一致的方式坚持这些专有命名方案。
如果要使类名唯一,只能给它加上Impl的后缀,那么您需要重新考虑是否有接口。所以当你有一个接口和一个单独的实现,而不是唯一专门化的接口时,你可能在大多数情况下不需要接口。
然而,一般来说,对于可维护性、可测试性和模拟,提供接口是最佳实践。更多细节请看这个答案。
也可以参考Martin Fowler关于InterfaceImplementationPair这个主题的有趣文章
TruckClass听起来像是Truck的一个类,我认为推荐的解决方案是添加Impl后缀。在我看来,最好的解决方案是在实现名称中包含一些信息,在特定的实现中发生了什么(就像我们有List接口和实现:ArrayList或LinkedList),但有时你只有一个实现,并且由于远程使用(例如)必须有接口,那么(如开头提到的)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)发生冲突。
我在这里看到的回答表明,如果你只有一个实现,那么你就不需要接口。这违背了依赖注入/控制反转原则(不要给我们打电话,我们会给你打电话!)。
So yes, there are situations in which you wish to simplify your code and make it easily testable by relying on injected interface implementations (which may also be proxied - your code doesn't know!). Even if you only have two implementations - one a Mock for testing, and one that gets injected into the actual production code - this doesn't make having an interface superfluous. A well documented interface establishes a contract, which can also be maintained by a strict mock implementation for testing.
事实上,您可以建立测试,让模拟实现最严格的接口契约(为不应该为空的参数抛出异常,等等)并在测试中捕获错误,在生产代码中使用更有效的实现(不检查不应该为空的参数是否为空,因为模拟在测试中抛出异常,并且由于在这些测试后修复了代码,您知道参数不为空)。
依赖注入/IOC对于新手来说很难理解,但是一旦你理解了它的潜力,你就会想要在任何地方都使用它,你会发现自己一直在做接口——即使只有一个(实际的生产)实现。
对于这个实现(您可以推断,并且您是正确的,我认为用于测试的模拟应该称为Mock(InterfaceName)),我更喜欢使用Default(InterfaceName)这个名称。如果出现更具体的实现,则可以适当地命名它。这也避免了我特别不喜欢的Impl后缀(如果它不是抽象类,它当然是“Impl”!)
我也更喜欢“基类(InterfaceName)”而不是“抽象(InterfaceName)”,因为在某些情况下,你希望你的基类以后成为可实例化的,但现在你被“抽象(InterfaceName)”这个名字卡住了,这迫使你重命名类,可能会造成一些小混乱-但如果它总是Base(InterfaceName),删除抽象修饰符不会改变类是什么。