在Java中实现接口时的默认vs Impl


34

阅读后包装名称应为单数还是复数?在我看来,我从未见过涉及我的宠儿之一的适当辩论:命名接口的实现。

假设您有一个Order旨在以各种方式实现的接口,但是在首次创建项目时只有初始实现。您是否选择DefaultOrderOrderImpl或采取其他措施来避免错误的二分法?当出现更多实现时,您会怎么做?

最重要的是...为什么?

Answers:


59

名称有机会传达含义。您为什么要放弃Impl的机会?

首先,如果您只有一个实现,则取消该接口。 它会创建此命名问题,并且不添加任何内容。更糟糕的是,如果您和所有其他开发人员不注意始终仅使用接口,则可能导致API中的方法签名不一致。

鉴于此,我们可以假定每个接口都有可能有两个或更多的实现。

  • 如果您现在只有一个,并且您不知道另一个可能会有所不同,那么Default是一个不错的开始。

  • 如果您现在有两个,请根据其用途分别命名。

    示例:最近,我们有一个具体的Context类(参考数据库)。已经认识到,我们需要能够表示一个脱机的上下文,因此名称Context被用于新接口(以保持与旧API的兼容性),并且创建了一个新的实现OfflineContext。但是猜猜原来的名字改成了什么吗?没错,ContextImpl(喜欢)。

    在这种情况下,DefaultContext可能还可以,人们会理解它,但它的描述性不如可能。毕竟,如果它不离线,那是什么?所以我们选择了:OnlineContext


特殊情况: 在接口上使用“ I”前缀

建议在接口上使用I前缀作为其他答案之一。最好,你没有需要做到这一点。

但是,如果您既需要一个接口来进行自定义实现,又要使用一个主要的具体实现,并且其基本名称过于简单以至于不能仅放弃一个接口,那么可以考虑添加界面上的“我”(不过,如果它仍然不适合您和您的团队,则完全可以)。

示例:许多对象可以是“ EventDispatcher”。为了使用API​​,它必须符合接口。但是,您还希望提供一个用于委派的基本事件分发程序。 DefaultEventDispatcher可以,但是有点长,如果经常要看到它的名称,您可能更喜欢为具体类使用基本名称EventDispatcher,并为自定义实现实现IEventDispatcher

/* Option 1, traditional verbose naming: */
interface EventDispatcher { /* interface for all event dispatchers */ }
class DefaultEventDispatcher implements EventDispatcher {
  /* default event dispatcher */
}

/* Option 2, "I" abbreviation because "EventDispatcher" will be a common default: */
interface IEventDispatcher { /* interface for all event dispatchers */ }
class EventDispatcher implements IEventDispatcher {
  /* default event dispatcher. */
}

3
+1为肯定的答案。就像不使用Impl的原因一样。
加里·罗

2
+1完全同意。将接口与它所代表的域概念分开命名完全没有意义。
rupjones 2011年

9
“如果只有一个实现”,您如何事先知道只有一个实现?“每个接口都有或可能有两个或多个实现”……在拥有两个实现之前,您首先没有,有一个,然后有两个,我的意思是,可能只有片刻只有一个实现,在实施第二个计划之前。
TulainsCórdova'13

2
@NickC我不是关于语义的pedantinc(我是诗人,我什至都不知道)。英语不是我的母语,所以我对此不屑一顾。我说的是有缺陷的逻辑。您可以使用接口进行解耦。不需要一定数量的实现。
TulainsCórdova'13

4
if you will only ever have one implementation, do away with the interface-除非您要测试组件,否则您可能需要保留该接口以便创建MockOrder,OrderStub或类似组件。
JBR威尔金森

15

我根据接口的用例决定命名。

如果接口用于解耦,那么我选择Impl实现。

如果接口的目的是行为抽象,则根据实现具体执行的方式来命名实现。我经常在其后附加接口名称。因此,如果调用该接口,则Validator使用FooValidator

我发现这Default是一个非常糟糕的选择。首先,它污染代码完成功能,因为名称始终以其开头。另一件事是默认值会随着时间的推移而变化。因此,过时的功能可能会在一段时间后成为默认值。因此,要么您总是在默认值更改后立即开始重命名您的类,要么就使用了具有误导性的名称。


8

我同意Nicole的回答(特别是在大多数情况下可能不需要该接口),但是为了讨论起见,我将抛出除OrderImpl和之外的其他替代方案DefaultOrder:将实现隐藏在静态工厂方法(如)之后Orders.create()。例如:

public final class Orders {
  public static Order create() {
    return new Order() {
      // Implementation goes here.
    };
  }
}

使用这种方法,实现可以是匿名内部类,也可以是名称中带有DefaultImpl名称的私有类,或者可以完全命名为其他名称。无论选择哪个选项,呼叫者都不必在意,因此无论现在还是以后决定更改时,您都可以获得更大的灵活性。

在实践中,这种模式的一些很好的例子是java.util.Collectionsand java.util.concurrent.Executors实用程序类,其方法返回隐藏的实现。正如有效的Java在项目1中提到的那样,此模式可以帮助使API的“概念权重”保持较小。


+1是一个有趣的附加案例:匿名实现。
加里·罗

1
也广泛用于番石榴
妮可(Nicole)2013年

3

OrderImpl之所以总是这样做只是因为它在Order界面后立即按字母顺序显示。


2
以及您如何处理正在进行的进一步实施,特殊处理等?
加里·罗

1
您询问了最初的实施情况。一旦开始实现DomesticOrder,ForeignOrder或任何其他内容,它们的命名就会更周到,而无需考虑它们在字母表中的位置。
本·霍夫斯坦

完全正确-我已经编辑了原始问题以反映此新要求。
加里·罗

这不是一个好主意,如果有不止一个实现。
萨德,

0

您可以使用前缀I(IWhatever)命名接口,然后实现实现。

正式的Java代码约定没有说明接口的这种命名方式,但是这种命名方式有助于识别和导航。


为什么在接口前面加上I?它有助于导航识别吗?
加里·罗

@Gary:以I作为接口类型的前缀是Delphi语言中公认的约定。在Delphi中,类的类型以T为前缀。因此,我们将具有IOrder接口和TOrder默认实现,并将TSomethingOrder和TBullMarketOrder作为特定实现。
Marjan Venema

3
我已经看到该约定在.NET开发中也大量使用。可能是因为Microsoft的文档和示例使用了它。
本·霍夫斯坦

5
似乎@Renesis提出了一个在Java中使用它的有用案例。就个人而言,我将依靠IDE来告诉我两者之间的区别,否则我们将E用作Enums,将C用作类,并且在很大程度上都是多余的。
加里·罗

0

我认为虽然在某些情况下使用Default可能有意义,但描述实现将更为有用。所以,如果你的界面UserProfileDAO那么你的实现可以是UserProfileSQLDAOUserProfileLDAPDAO或类似的东西。


0

如果可能,请以其功能/方式来命名。

通常最糟糕的方法是在应如何使用之后对其进行命名。

如果应该将其用作实现的基类,则可以使用BaseX或AbstractX(如果它是抽象的(但是请尝试对其进行压缩,因为如果它不执行任何操作,则您将不会创建它)接口)如果它提供了最简单的功能,并且在该功能足够时可以直接使用(不扩展),则可以将其称为SimpleX或BasicX。

如果使用它(除非提供了其他实现),则将其命名为DefaultX


-2

这些答案大多数都描述了正在做的事情,而不是为什么。

OO原则发生了什么?关于课程,Impl告诉我什么以及如何使用它?您应该关注的是了解用法,而不是其构造方式。

如果创建一个Person接口,那么PersonImpl是什么?无意义的。至少DefaultPerson告诉我,无论是谁编码的,都不应创建接口。

接口正在输入多态性,应按原样使用。


1
@NickC接受的答案详细介绍了正在执行的操作以及原因。
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.