大多数OO语言的接口名称都带有大写字母I,为什么Java不这样做?不遵守这一惯例的理由是什么?
为了证明我的意思,如果我想拥有一个用户界面和一个用户实现,我在Java中有两个选择:
- Class = User,Interface = UserInterface
- Class = UserImpl,Interface = User
醇>
大多数语言的地方:
Class = User,Interface = IUser
现在,您可能会争辩说,您总是可以为用户实现选择一个最具描述性的名称,但问题就会消失,但Java正在推动POJO方法,而大多数IOC容器都广泛使用DynamicProxies。这两个因素共同意味着您将拥有大量具有单个POJO实现的接口。
所以,我想我的问题归结为:“是否值得遵循更广泛的接口命名约定,特别是考虑到Java框架似乎在哪里?”
答案 0 :(得分:307)
我不想在接口上使用前缀:
前缀会影响可读性。
在客户端中使用接口是标准的最佳编程方式,因此接口名称应尽可能短且令人愉快。实施课程应该更加难以阻止其使用。
当从抽象类更改为接口时,带有前缀I的编码约定意味着重命名该类的所有实例---不好!
答案 1 :(得分:106)
真的有区别:
class User implements IUser
和
class UserImpl implements User
如果我们所讨论的只是命名约定?
我个人更喜欢不在I
接口之前,因为我想编码接口,我认为 在命名约定方面更重要。如果您调用接口IUser
,那么该类的每个消费者都需要知道它IUser
。如果你打电话给班级UserImpl
,那么只有班级和你的DI容器知道Impl
部分,而消费者只知道他们正在使用User
。
然后,我被迫使用Impl
的次数因为一个更好的名字本身并不存在,因为实现被命名为根据到实施,因为这是重要的,例如
class DbBasedAccountDAO implements AccountDAO
class InMemoryAccountDAO implements AccountDAO
答案 2 :(得分:74)
Java通常不会使用IUser约定可能有几个原因。
面向对象方法的一部分是您不必知道客户端是使用接口还是实现类。因此,即使List是一个接口,String也是一个实际的类,一个方法可能会同时传递它们 - 在视觉上区分接口是没有意义的。
通常,我们实际上更喜欢在客户端代码中使用接口(例如,更喜欢List to ArrayList)。因此,使接口脱颖而出是没有意义的。
Java命名约定更喜欢具有匈牙利式前缀的实际含义的较长名称。因此代码将尽可能可读:List表示列表,User表示用户 - 而不是IUser。
答案 3 :(得分:70)
许多开源项目(包括Spring)也使用了另一种约定。
interface User {
}
class DefaultUser implements User {
}
class AnotherClassOfUser implements User {
}
我个人不喜欢“I”前缀,原因很简单,因为它是一个可选的约定。那么,如果我采用这个IIOPConnection意味着IOPConnection的接口?如果该类没有“I”前缀,那么我是否知道它不是一个接口...这里的答案是否定的,因为并不总是遵循约定,并且监管它们将创建更多的约定本身保存的工作。 / p>
答案 4 :(得分:44)
正如另一张海报所说,通常最好让接口定义功能而不是类型。我倾向于不“实现”类似“用户”的东西,这就是为什么“IUser”通常不是以这里描述的方式所必需的。我经常把类看作名词和接口作为形容词:
class Number implements Comparable{...}
class MyThread implements Runnable{...}
class SessionData implements Serializable{....}
有时形容词没有意义,但我通常仍然使用接口来模拟行为,动作,功能,属性等......而不是类型。
此外,如果您真的只打算制作一个用户并将其称为用户,那么还有一个IUser界面的重点是什么?如果你想要有一些不同类型的用户需要实现一个通用接口,那么在接口上添加“I”可以节省你选择实现的名称吗?
我认为一个更现实的例子是某些类型的用户需要能够登录到特定的API。我们可以定义一个Login接口,然后有一个“User”父类,包含SuperUser,DefaultUser,AdminUser,AdministrativeContact等等,其中一些将根据需要实现Login(Loginable?)界面或不实现。
答案 5 :(得分:36)
Bob Lee在演讲中曾说过:
如果你是接口的话 只有一个实现。
所以,你从一个实现开始,即没有接口。 稍后你决定,这里需要一个接口,所以你将你的类转换为接口。
然后很明显:你的原始类叫做User。您的界面现在称为用户。也许你有UserProdImpl和UserTestImpl。如果你很好地设计了你的应用程序,那么每个类(实例化用户的那个)都将保持不变,并且不会注意到它们会突然传递给接口。
所以它变得清晰 - >接口用户实现UserImpl。
答案 6 :(得分:23)
在C#中它是
public class AdminForumUser : UserBase, IUser
Java会说
public class AdminForumUser extends User implements ForumUserInterface
正因为如此,我不认为约定在接口的java中几乎同样重要,因为继承和接口实现之间存在明显的区别。我只想选择你想要的任何命名约定,只要你是一致的并使用一些东西向人们展示这些是接口。几年没有完成java,但所有接口都只是在他们自己的目录中,这就是惯例。从来没有真正遇到任何问题。
答案 7 :(得分:6)
根据我的经验,“I”约定适用于旨在为类提供契约的接口,特别是当接口本身不是类的抽象概念时。
例如,在您的情况下,如果您打算拥有的唯一用户是IUser
,我只希望看到User
。如果您计划使用不同类型的用户 - NoviceUser
,ExpertUser
等 - 我希望看到User
接口(也许还有AbstractUser
类实现了一些常用功能,如get/setName()
)。
我还希望定义功能的接口 - Comparable
,Iterable
等 - 能够像这样命名,而不是像IComparable
或IIterable
。
答案 8 :(得分:5)
遵循良好的OO原则,您的代码应该(尽可能/可能)依赖于抽象而不是具体的类。例如,通常最好编写一个这样的方法:
public void doSomething(Collection someStuff) {
...
}
比这个:
public void doSomething(Vector someStuff) {
...
}
如果您遵循这个想法,那么我认为如果您提供类似“User”和“BankAccount”(例如)的接口名称,而不是“IUser”,“UserInterface”或其他变体,您的代码将更具可读性。
应该关注实际具体类的代码的唯一部分是构造具体类的地方。其他所有内容都应该使用接口编写。
如果你这样做,那么“丑陋”的具体类名如“UserImpl”应该安全地隐藏在代码的其余部分,这可以继续使用“漂亮”的接口名称。
答案 9 :(得分:2)
= v = Wicket框架中也使用了“I”前缀,我很快习惯了它。总的来说,我欢迎任何缩短繁琐的Java类名的约定。但是,在目录和Javadoc中,“I”下的所有内容都按字母顺序排列,这很麻烦。
Wicket编码实践类似于Swing,因为许多控件/窗口小部件实例被构造为具有内联方法声明的匿名内部类。令人讨厌的是,它与Swing的180°不同,因为Swing使用前缀(“J”)来实现类。
“Impl”后缀是一个狡猾的缩写,并没有很好的国际化。如果我们至少和“Imp”一起消失的话,那就更可爱(也更短)。 “Impl”用于IOC,尤其是Spring,所以我们暂时还是坚持使用它。不过,在一个代码库的三个不同部分遵循3种不同的约定,它有点精神错乱。
答案 10 :(得分:0)
这是否是真正意义上更广泛的命名约定?我更多的是在C ++方面,而不是Java和后代。有多少语言社区使用I惯例?
如果您在此处拥有与语言无关的商店标准命名约定,请使用它。如果没有,请使用语言命名约定。