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