Java中的接口命名

Interface naming in Java

大多数OO语言都用一个资本i来前缀他们的接口名称,为什么Java不这么做呢?不遵守本公约的理由是什么?

为了证明我的意思,如果我想拥有一个用户界面和一个用户实现,我将在Java中有两个选择:

  • Class = User, Interface = UserInterface
  • Class = UserImpl, Interface = User
  • 在大多数语言中:

    Class = User, Interface = IUser

    现在,您可能会争辩说,您总是可以为用户实现选择一个最具描述性的名称,问题就消失了,但是Java推动Pojo方法来处理事情和大多数IOC容器都使用动态代理。这两件事结合在一起意味着您将拥有许多具有单个POJO实现的接口。

    所以,我想我的问题归结为:"是否值得遵循更广泛的接口命名约定,特别是考虑到Java框架的方向?"


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

    • 前缀损害可读性。

    • 在客户机中使用接口是标准的最佳编程方法,因此接口名称应尽可能短且令人愉快。实现类应该更丑,以阻止它们的使用。

    • 当从抽象类更改为接口时,具有前缀i的编码约定意味着重命名类的所有出现位置---不好!


    以下两者之间是否真的存在差异:

    1
    class User implements IUser

    1
    class UserImpl implements User

    如果我们所说的只是命名约定?

    就我个人而言,我不喜欢在与I的接口之前,因为我想对接口进行编码,我认为这在命名约定方面更重要。如果调用接口IUser,那么该类的每个使用者都需要知道它的IUser。如果您调用类UserImpl,那么只有类和您的DI容器知道Impl部分,并且消费者只知道他们正在使用User

    另外,由于没有更好的名称,我被迫使用Impl的次数非常少,因为实现是根据实现来命名的,因为这是很重要的,例如。

    1
    2
    class DbBasedAccountDAO implements AccountDAO
    class InMemoryAccountDAO implements AccountDAO


    可能有几个原因Java一般不使用IUSER约定。

  • 面向对象方法的一部分是,您不必知道客户机是使用接口还是实现类。所以,即使列表是一个接口,字符串也是一个实际的类,一个方法可能同时被传递给这两个类——从视觉上区分接口是没有意义的。

  • 一般来说,我们更喜欢在客户机代码中使用接口(例如,更喜欢列表而不是arraylist)。因此,让接口作为异常突出是没有意义的。

  • Java命名约定更喜欢具有匈牙利风格前缀的实际名称。这样代码将尽可能可读:一个列表代表一个列表,一个用户代表一个用户,而不是一个iuser。


  • 还有另一个约定,被包括Spring在内的许多开源项目使用。

    1
    2
    3
    4
    5
    6
    7
    8
    interface User {
    }

    class DefaultUser implements User {
    }

    class AnotherClassOfUser implements User {
    }

    我个人不喜欢"i"前缀,原因很简单,它是一个可选的约定。所以如果我采用这一点,IIopConnection是否意味着IOPConnection的接口?如果类没有"i"前缀,那么我是否知道它不是一个接口……这里的答案是"否",因为并不总是遵循约定,并且对它们进行管理将创建约定本身保存的更多工作。


    正如另一个海报所说,通常情况下,最好让接口定义功能,而不是类型。我倾向于不"实现"类似"用户"的东西,这就是为什么"i user"在这里描述的方式中通常不是真正必要的。我经常把类看作名词,把接口看作形容词:

    1
    2
    3
    class Number implements Comparable{...}  
    class MyThread implements Runnable{...}
    class SessionData implements Serializable{....}

    有时形容词没有意义,但我仍然通常使用接口来建模行为、动作、能力、属性等。不是类型。

    另外,如果你真的只想让一个用户称之为用户,那么拥有一个iuser接口又有什么意义呢?如果您有几个不同类型的用户需要实现一个公共接口,那么在接口上附加一个"i"会使您在选择实现的名称时节省什么呢?

    我认为一个更现实的例子是,某些类型的用户需要能够登录到特定的API。我们可以定义一个登录界面,然后用superuser、defaultuser、adminuser、administrativecontact等类创建一个"用户"父类,其中一些类将实现或不实现登录(loginable?)必要时提供接口。


    鲍勃李在一次演讲中说:

    whats the point of an interface if you
    have only one implementation.

    因此,您从一个实现开始,即没有接口。稍后您决定,这里需要一个接口,所以您可以将类转换为接口。

    然后很明显:您的原始类被称为用户。您的界面现在称为用户。也许你有一个userprodimpl和一个usertestimpl。如果您的应用程序设计得很好,那么每个类(实例化用户的类除外)都将保持不变,并且不会注意到它们突然通过了一个接口。

    这样就清楚了->界面用户实现userimpl。


    在C中,它是

    1
    public class AdminForumUser : UserBase, IUser

    Java会说

    1
    public class AdminForumUser extends User implements ForumUserInterface

    因此,在继承接口和接口实现之间存在明显的差异,我认为在Java中,约定对于接口几乎不重要。我想说的是,只要您是一致的,并且使用一些东西向人们展示这些是接口,就可以选择您想要的任何命名约定。在几年内还没有做过Java,但是所有的接口都在自己的目录中,这就是惯例。从来没有真正的问题。


    根据我的经验,"i"约定适用于旨在向类提供契约的接口,特别是当接口本身不是类的抽象概念时。

    例如,在您的情况下,我只希望看到IUser,如果您唯一想要的用户是User。如果您计划有不同类型的用户-NoviceUserExpertUser等-我希望看到一个User接口(可能还有一个AbstractUser类,它实现一些公共功能,如get/setName())。

    我还希望定义功能的接口——ComparableIterable等——可以这样命名,而不像IComparableIIterable


    遵循良好的OO原则,您的代码应该(尽可能)依赖于抽象,而不是具体的类。例如,编写这样的方法通常更好:

    1
    2
    3
    public void doSomething(Collection someStuff) {
        ...
    }

    比这个:

    1
    2
    3
    public void doSomething(Vector someStuff) {
        ...
    }

    如果您遵循这个想法,那么我坚持认为,如果您给接口命名如"用户"和"银行帐户"(例如),而不是"i user"、"用户界面"或其他变体,您的代码将更易于阅读。

    唯一应该关心实际具体类的代码位是构建具体类的地方。其他一切都应该使用接口编写。

    如果这样做,那么"丑陋的"具体类名(如"userimpl")应该被安全地隐藏在代码的其余部分,这样可以愉快地继续使用"nice"接口名。


    =v=wicket框架中也使用了"i"前缀,我很快就习惯了。一般来说,我欢迎任何缩短繁琐Java类名称的约定。不过,在目录和javadoc中,所有东西都按字母顺序排列在"i"下,这是一个难题。

    Wicket编码实践类似于Swing,因为许多控件/小部件实例被构造为带有内联方法声明的匿名内部类。令人恼火的是,它与Swing的180度不同之处在于Swing对实现类使用了前缀("j")。

    "impl"后缀是一个冗长的缩写,不能很好地国际化。如果我们至少和"小鬼"一起走,那就更可爱(更短)。IMPL"用于IOC,尤其是Spring,所以我们现在有点纠结于此。不过,它在一个代码库的三个不同部分中遵循3种不同的约定,会有点分裂。


    这是一个更广泛的命名约定吗?我更多的是C++方面,而不是真正的Java和后代。有多少语言社区使用i约定?

    如果您在这里有一个独立于语言的商店标准命名约定,请使用它。如果没有,请使用语言命名约定。