Java中的接口命名

368
大多数面向对象语言都将它们的接口名称以大写字母I为前缀,那么Java为什么不这样做呢?为什么不遵循这个约定?为此有什么理由?
举个例子,如果我想要一个用户界面和用户实现,我在Java中有两个选择: 1. 类名=User, 接口名=UserInterface 2. 类名=UserImpl, 接口名=User 而在大多数其他语言中,规范如下: 类名=User, 接口名=IUser 你可能会说,你总是可以为用户实现选择一个最描述性的名称,这个问题就解决了,但Java推崇一种POJO(Plain Old Java Object)的方法,并且大多数IOC容器广泛使用DynamicProxies,这两件事情共同意味着你将有许多只有单个POJO实现的接口。
因此,我的问题归结为:“特别是考虑到Java框架的发展方向,是否值得遵循更广泛的接口命名约定?”

70
大多数语言中的哪些部分?除了 .Net 之外,还有哪些语言? - wds
3
不同的语言社区有不同的命名惯例,这种情况已经存在了很长时间。了解命名惯例的起源有时需要进行民俗学研究。 - David Thornley
5
Eclipse是一个重要的例外,它使用Java编程语言,并采用了IFoo式的命名风格。详情请参考http://wiki.eclipse.org/Naming_Conventions。 - David Leonard
3
如果您查看Android SDK(以及Java)中接口的名称,您会发现它们采用了在接口名称末尾添加"Interface"一词的命名惯例,例如NetworkInterfaceDialogInterface等。 - sandalone
34
这个问题为什么会被关闭为“不具建设性”,明明很受欢迎,许多人都感兴趣呢? - inor
显示剩余9条评论
11个回答

367

我不喜欢在接口上使用前缀:

  • 前缀会降低可读性。

  • 在客户端中使用接口是最佳编程方式,因此接口的名称应尽可能短且易读。实现类应该更难看一些以防止它们被使用。

  • 从抽象类转换为接口时,使用带有前缀 I 的编码约定意味着需要重命名类的所有出现——这并不好!


13
完全同意。如果您使用自动完成功能,还需要输入一个关键字。有很多以 I 开头的文件。 - Kalecser
90
任何一款像样的集成开发环境都可以轻松地按照你描述的方式更改代码。同时,当从抽象类转变为接口时,我相信你的客户端无论如何都需要重新编译。 - Allain Lalonde
67
这里很晚了,但是:如果一个IDE使得更改某个东西的名称变得容易,这完全没有关系。使用某种类型的实例的代码永远不应该在乎该类型是接口还是类或其他什么。因此,在类型的名称中暴露这样的细节是毫无意义的,有害于理解。重要的是类型和其暴露的合同! - ColinD
11
另外,如果您选择使用IFoo的方式,那么实现应该是CFoo,当然,在失败时,方法会抛出一个EFoo。 - Robin
68
“前缀会影响可读性”这种说法纯属主观。大多数命名规范也是如此。更重要的是,你的团队达成一致意见,使代码库保持一致性。 - dreadwail
显示剩余10条评论

135

这两者之间真的有区别吗:

class User implements IUser

并且

class UserImpl implements User

如果我们只是谈论命名约定,个人而言,我更喜欢不在接口前加上 I,因为我想要编写针对接口的代码,并且我认为这在命名约定方面更为重要。如果您将接口称为 IUser,则该类的每个消费者都需要知道它是一个 IUser。如果您将类称为 UserImpl,那么只有类和 DI 容器知道 Impl 的部分,而消费者只知道他们正在使用一个User

再次说一下,因为没有更好的名称出现,所以被迫使用 Impl 的时候很少很少,因为实现根据其实现方式进行命名,因为那才是重要的地方,例如:

class DbBasedAccountDAO implements AccountDAO
class InMemoryAccountDAO implements AccountDAO

6
DAO是一种广为人知的数据库访问模式,因此将该模式包含在类名中使得仅通过查看其名称就能轻松了解该类所执行的操作。P.S. 建议严格遵循驼峰命名法("AccountDao"), 如同 http://mina.apache.org/changes-between-2x-and-1x.html。 - Esko Luontola
4
@marker,这并不像使用"I"来指示接口那样简单。DAO告诉你该类或接口是一个用于访问账户数据的对象,也就是该对象的作用。而使用"I"只是告诉你它是接口,与对象本身无关,仅涉及语言语义。 - tddmonkey
3
不,这是关于语义学(“DAO”)和语言结构的不必要装饰(例如接口中的“我”;这个事实已经在代码中提到了,“interface IFoo...”)。 - Dirk
在这个特定的例子中,我看不出将实现与接口分开的理由。UserImpl并没有说明User是如何实现的。实现的名称应该让这一点变得明确。 - dzieciou
以语义化的方式命名实现和接口,我非常同意您的观点。 - mochadwi
显示剩余4条评论

83

Java普遍不使用IUser约定的原因可能有几个。

  1. 面向对象编程的一部分是,你不应该知道客户端是否在使用接口还是实现类。因此,即使List是一个接口而String是一个实际类,一个方法可以传递它们两个 - 把接口视觉上区别开来没有意义。

  2. 通常情况下,我们实际上更喜欢在客户端代码中使用接口(例如,优先使用List而不是ArrayList)。因此,将接口作为异常显眼地突出并不合理。

  3. Java命名约定更喜欢具有实际含义的较长名称而不是匈牙利式前缀。因此,代码尽可能易读:List代表列表,User代表用户 - 而不是IUser。


82

还有另一种约定,许多开源项目包括Spring在内都使用这种约定。

interface User {
}

class DefaultUser implements User {
}

class AnotherClassOfUser implements User {
}

我个人并不喜欢“I”前缀,因为这只是一种可选的约定。 如果我采用这种前缀,那么IIOPConnection是否意味着IOPConnection接口?如果类没有“ I”前缀,我如何知道它不是接口?答案是否定的,因为约定并不总是遵循,而且强制执行它们会比约定本身节省的工作还要多。


我喜欢这个,因为它还有助于让你进入使用接口来处理外部依赖关系的模式,而不是将类耦合在一起的状态。 - Matt Briggs

55

正如另一个发帖者所说,通常更喜欢接口定义能力而不是类型。我倾向于不去“实现”像“用户”这样的东西,这就是为什么在这里描述的方式中常常没有必要使用“IUser”的原因。我经常将类看作名词,接口看作形容词:

class Number implements Comparable{...}  
class MyThread implements Runnable{...}
class SessionData implements Serializable{....}
有时形容词可能没有意义,但我通常会使用接口来模拟行为、操作、能力和属性等,而不是类型。 此外,如果您真的只打算创建一个名为“User”的用户,那么还有必要有一个IUser接口吗?如果您将拥有几种不同类型的用户需要实现公共接口,则在接口名称后追加"I"能在选择实现名称方面为您节省什么呢? 我认为一个更现实的例子是,一些类型的用户需要能够登录到特定的API。我们可以定义一个Login接口,然后有一个“User”父类与SuperUser、DefaultUser、AdminUser、AdministrativeContact等子类,其中一些将根据需要实现或不实现Login(Loginable?)接口。

我认为你提供的支持“接口作为形容词”的例子是有偏见的,因为这些接口旨在更像混合(或特征)。因此,接口既适用于形容词又适用于名词 - 但可能不适用于“不定及物动词”8^P。 - Stevel
这可能在最近几年有所改变。Oracle文档允许接口作为类型:[链接] http://docs.oracle.com/javase/tutorial/java/IandI/interfaceAsType.html - karlihnos

43

在一次演讲中,Bob Lee曾说:

如果你只有一个实现,接口的意义在哪里?

所以,一开始你只有一个实现,也就是没有接口。以后你可能会认为,在这里需要一个接口,于是将类转换为接口。

然后很明显:你最初的类名叫做User,现在你的接口也是叫做User。也许你有一个UserProdImpl和一个UserTestImpl。如果你的应用程序设计得好,那么除了实例化User的类之外,每个类都不会改变,并且不会注意到突然间它们被传递了一个接口。

因此,这变得清晰 -> 接口User实现UserImpl。


25
使用接口有助于测试驱动开发。因为我们决定不使用接口,所以我们在领域模型和测试方面遇到了许多问题。我的一个朋友曾经说过:“一切都是接口,在长远的发展中它会拯救你。” - hooknc
5
嘲讽和简单的代理支持。我相信还有其他提供接口的原因。 - MetroidFan2002
3
最近我对一位同事说过,现在引入一个接口不需要任何成本,但以后可以为你节省大量时间。 - tddmonkey
2
如果您在不久的将来可能会有其他实现,那么接口将是一个加分项。 - Stef
3
我不喜欢“未来可能需要另一种实现”的论点。我更倾向于采用YAGNI方法:不要为尚未发生的事情做计划。 - David Lavender
显示剩余7条评论

25

在 C# 中

public class AdminForumUser : UserBase, IUser

Java会说:

public class AdminForumUser extends User implements ForumUserInterface
因此,我认为在Java中接口的约定并不像继承和接口实现之间那样重要。 我会建议您选择任何想要的命名约定,只要您保持一致,并使用某些东西来显示这些是接口即可。几年没有做Java了,但所有的接口都在它们自己的目录中,这就是约定。 从来没有遇到过任何问题。

24
也许我不是很擅长Java编程,但我更喜欢C#的风格,也就是所有接口都以'I'前缀开头 :) - davs
10
我不确定你和其他人从哪里得到这个所谓的约定。在Java库中并没有明显体现…… - ChiefTwoPencils
1
虽然这是正确的,但问题在于区分方法内部的接口和类(例如)。即 public void AddUser(IClient client, User user)。在这个例子中,你可以看出客户端是一个接口,而用户是一个类。 - Hummus

8
根据我的经验,“I”约定适用于旨在为类提供合同的接口,特别是当接口本身不是类的抽象概念时。
例如,在您的情况下,如果您只打算拥有一个用户,则只会看到“ IUser”。如果您计划拥有不同类型的用户-“ NoviceUser”,“ ExpertUser”等-我会期望看到一个“ User”接口(也许还有一个实现一些公共功能的“ AbstractUser”类,例如“ get / setName()”)。
我还期望定义能力的接口-“ Comparable”,“ Iterable”等-像这样命名,而不是像“ IComparable”或“ IIterable”。

如果您只打算拥有一个用户,为什么不直接使用User而不是接口呢? - Carighan
3
在某些客户端/服务器架构中,客户端需要了解接口,而无需重量级的服务器实现。 - David Koelle

6

遵循良好的面向对象原则,你的代码应该(在实际/可能的情况下)依赖于抽象而不是具体类。例如,编写如下方法通常更好:

public void doSomething(Collection someStuff) {
    ...
}

比这个更好:
public void doSomething(Vector someStuff) {
    ...
}

如果你遵循这个想法,那么我认为,如果你给接口命名为“User”和“BankAccount”(例如),而不是“IUser”,“UserInterface”或其他变体,你的代码将更易读。
唯一关心实际具体类的代码应该是构建实际具体类的地方。其他所有代码都应该使用接口编写。
如果你这样做,“丑陋”的具体类名称,如“UserImpl”,应该安全地隐藏在其余代码中,可以愉快地继续使用“好看”的接口名称。

但是,当您只需要为一个或两个事物创建接口时,为程序中的每个类制作匹配接口有何必要呢? - LegendLength

3

=v= “I”前缀在Wicket框架中也被使用,我很快就习惯了它。总的来说,我欢迎任何可以缩短繁琐Java类名的约定。然而,这让一切都以“ I”字母开头在目录和Javadoc中排列有些麻烦。

Wicket编码实践类似于Swing,在其中许多控件/小部件实例构造为带有内联方法声明的匿名内部类。令人不爽的是,它与Swing完全相反,因为Swing使用一个前缀(“ J”)表示实现类。

“Impl”后缀是一个略显啰嗦的缩写,国际化效果不好。如果我们至少改成了“Imp”,那就会更可爱(而且更短)。“Impl”被用于IOC,特别是Spring,所以我们现在被困在这里。遵循三个不同约定的三个不同代码库的规范有点疯狂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接