Java中的接口命名
大多数OO语言都是用大写字母I来表示它们的接口名称,为什么Java不这样做呢? 没有遵循这个惯例的理由是什么?
为了演示我的意思,如果我想要一个用户界面和一个用户实现,我会在Java中有两个select:
- Class = User,Interface = UserInterface
- Class = UserImpl,Interface = User
在大多数语言中:
Class = User,Interface = IUser
现在,你可能会争辩说,你总是可以为用户实现select一个最具描述性的名字,问题就会消失,但是Java将POJO方法推向了事物,大多数IOC容器都广泛地使用了DynamicProxies。 这两件事意味着你将有一个单一的POJO实现的接口。
所以,我想我的问题归结为: “是否值得遵循更广泛的接口命名约定,特别是鉴于Java框架似乎在哪里?
我不想在接口上使用前缀:
-
前缀会伤害可读性。
-
在客户端使用接口是编程的标准最佳方式,因此接口名称应尽可能短而宜人。 实施class级应该是丑陋的,以阻止他们的使用。
-
当从抽象类更改为接口时,带有前缀I的编码约定意味着重命名该类的所有事件—不好!
真的有区别:
class User implements IUser
和
class UserImpl implements User
如果我们所说的是命名约定?
就我个人而言,我更喜欢不在与“I”的接口之前,因为我想对接口进行编码,我认为在命名约定方面更重要。 如果您调用IUser接口,则该类的每个消费者都需要知道其IUser。 如果您调用UserImpl类,那么只有类和DI容器知道Impl部分,而消费者只知道他们正在与用户一起工作。
再说一次,我被迫使用Impl的时代,因为更好的名字本身并没有出现,因为实现是根据实现来命名的,因为这是重要的,例如
class DbBasedAccountDAO implements AccountDAO class InMemoryAccountDAO implements AccountDAO
Java通常不会使用IUser约定可能有几个原因。
-
面向对象方法的一部分是,你不应该知道客户端是使用接口还是实现类。 所以,即使List是一个接口,而String是一个实际的类,一个方法可能会被传递给它们两个 – 对于直观地区分接口是没有意义的。
-
一般来说,我们实际上更喜欢在客户端代码中使用接口(比如喜欢List到ArrayList)。 因此,使接口脱颖而出是没有意义的。
-
Java命名约定优先select具有匈牙利式前缀的实际含义的较长名称。 所以代码将尽可能可读:一个List代表一个列表,一个User代表一个用户 – 而不是IUser。
还有另一个约定,许多开源项目(包括Spring)都使用这个约定。
interface User { } class DefaultUser implements User { } class AnotherClassOfUser implements User { }
我个人不喜欢“我”前缀的简单原因,它的一个可选的约定。 所以如果我采用这个IIOPConnection意味着IOPConnection的接口? 如果这个类没有“I”前缀,那么我知道它不是一个接口吗?这里的答案是否定的,因为约定并不总是被遵循,并且对它们进行pipe理将会创造更多的约定本身保存的工作。
正如另一张海报所说,通常最好让界面定义不是types的function。 我倾向于不实施像“用户”这样的东西,这就是为什么“IUser”往往不是这样描述的方式真正需要的。 我经常把名词和接口看作是形容词:
class Number implements Comparable{...} class MyThread implements Runnable{...} class SessionData implements Serializable{....}
有时一个形容词没有意义,但我仍然通常使用接口来build模行为,动作,能力,属性等等…不是types。
另外,如果你真的只打算让一个用户称之为用户,那么拥有一个IUser接口又有什么意义呢? 如果你将有一些需要实现一个通用接口的不同types的用户,那么在接口上附加一个“I”会不会让你select实现的名字?
我认为一个更现实的例子是某些types的用户需要能够login到一个特定的API。 我们可以定义一个Login接口,然后有一个带有SuperUser,DefaultUser,AdminUser,AdministrativeContact等等的“User”父类,其中一些将根据需要实现Login(Loginable)接口。
鲍勃李在演讲中曾经说过:
如果你只有一个实现,接口的意义何在。
所以,你从一个实现开始,即没有接口。 稍后你决定,这里需要一个接口,所以你把你的类转换成一个接口。
那么很明显:您的原始类被称为“用户”。 你的界面现在被称为用户。 也许你有一个UserProdImpl和一个UserTestImpl。 如果你devise好你的应用程序,每个类(除了实例化User)都将保持不变,并且不会注意到它们突然通过了一个接口。
所以它变得清晰 – >接口用户实现UserImpl。
在C#中是
public class AdminForumUser : UserBase, IUser
Java会说
public class AdminForumUser extends User implements ForumUserInterface
正因为如此,我不认为约定在接口的java几乎是重要的,因为inheritance和接口实现之间有一个明显的区别。 我会说只要select你想要的任何命名约定,只要你是一致的,并用一些东西来显示这些是接口。 几年之后还没有做过java,但是所有的接口都只是在他们自己的目录中,那就是惯例。 从来没有真正有任何问题。
遵循良好的面向对象的原则,你的代码应该(尽可能地)取决于抽象而不是具体的类。 例如,编写一个像这样的方法通常会更好:
public void doSomething(Collection someStuff) { ... }
比这个:
public void doSomething(Vector someStuff) { ... }
如果你遵循这个想法,那么我认为,如果你给出接口名称,比如“User”和“BankAccount”,而不是“IUser”,“UserInterface”或其他变体,那么你的代码将更具可读性。
应该关心实际具体类的唯一代码是具体类构造的地方。 其他的一切都应该使用接口来编写。
如果你这样做,那么像“UserImpl”这样的“丑陋的”具体类名应该被安全地隐藏起来,而不是其他的代码,它们可以使用“nice”接口名称来快速地继续。
根据我的经验,“I”约定适用于旨在为类提供契约的接口,特别是当接口本身不是类的抽象概念时。
例如,在你的情况下,我只希望看到IUser
如果你打算有唯一的用户是User
。 如果你打算拥有不同types的用户NoviceUser
, ExpertUser
等等 – 我期望看到一个User
界面(也许是一个AbstractUser
类,它实现了一些常用的function,比如get/setName()
)。
我也希望定义能力的接口 – Comparable
, Iterable
等 – 可以这样命名,而不是像IComparable
或IIterable
。
= v = Wicket框架中也使用了“I”前缀,我已经习惯了它。 一般来说,我欢迎任何缩短繁琐Java类名称的约定。 然而,在目录和Javadoc中,“I”下的所有内容都是按照字母顺序排列的。
Wicket编码实践与Swing类似,因为许多控件/小部件实例被构造为具有内联方法声明的匿名内部类。 令人烦恼的是,它与Swing相差180度,因为Swing使用前缀(“J”)作为实现类。
“Impl”后缀是一个蹩脚的缩写,并没有很好的国际化。 如果只有我们至less用“小鬼”去了,它会是可以(而且更短)。 “Impl”用于国际奥委会,特别是Spring,所以我们现在还在坚持。 虽然在一个代码库的三个不同部分遵循三种不同的约定,但它有点分裂。
这真的是一个更广泛的命名约定吗? 我更多的是C ++方面,而不是真正的Java和后代。 有多less种语言社区使用I惯例?
如果您在这里有独立于语言的商店标准命名惯例,请使用它。 如果没有,请使用语言命名约定。