接口名称是否应该以“ I”前缀开头?


73

我一直在阅读Robert Martin的“ Clean Code ”,希望成为一个更好的程序员。尽管到目前为止还没有一个真正的突破性进展,但是它使我对我设计应用程序和编写代码的方式有了不同的看法。

我不仅同意书中的一部分,而且对我没有任何意义,特别是关于接口命名约定。这是直接从书中摘录的文字。我将这方面加粗了,我感到困惑,并希望澄清。

我更喜欢不修饰界面。前面的I在当今的一堆旧书中很常见,充其量可以使人分心,而在最坏的情况下则可以提供过多的信息。我不想让用户知道我正在给他们一个接口

也许是因为我只是一个学生,或者可能是因为我从未做过任何基于专业或团队的编程,但是我希望用户知道它是一个界面。 实现接口和扩展类之间有很大的区别。

因此,我的问题归结为:“为什么我们应该隐藏一部分代码期望接口的事实?”

编辑

回答:

如果您的类型是接口或类是您的业务,而不是使用您的代码的人的业务。因此,您不应在此第三方代码中泄漏代码的详细信息。

我为什么不“泄漏”给定类型是第三方代码的接口还是类的详细信息?对于使用我的代码的第三方开发人员来说,知道他们将要实现接口还是扩展类不是很重要吗?差异是否不如我要记住的那么重要?


3
我同意你的观点。有时候隐藏太多信息不是很有帮助。但是,即使遵循此准则,您仍然可以使用IDE或附加组件来分辨类型。
NoChance 2011年

3
这个问题在本质上被称为“匈牙利表示法”问题,您应该找到很多争论,以及大多数非MS开发人员在此关键字下放弃它的原因。匈牙利符号在变量中普遍存在,但在类型上基本上相同。
thiton 2011年

2
先前的标题编辑非常糟糕。这个问题一般来说与匈牙利表示法无关,只是因为它提到了可能与之相关的约定。HN的相对优劣在这里完全无关紧要;问题主要是关于接口与类的关系,以及语义差异是否足够重要/有趣,足以证明特殊情况下的命名约定。
亚伦诺特,2011年

Re to know whether they will be implementing an interface or extending a class:是的,但是您代码的大多数用户都会调用它,而不实现或扩展它,而他们真的不在乎它是什么。
david.pfx 2014年

对于它的价值,我非常喜欢“ I”前缀。出于相同的原因,我还在抽象类上使用了“抽象”前缀。它对类/接口的使用者没有影响,但对需要提供它的实例的人却有很大的影响,对于其他正在阅读您代码的开发人员来说,它也更简单。这意味着他们可以一目了然地看到正在处理的内容,而不必根据具体情况咨询IDE以获取更多信息。我刚刚开始使用Angular,发现他们不遵守该约定真是令人讨厌!
Dan King

Answers:


66

如果您停止思考,您会发现接口在语义上与抽象类并没有太大不同:

  • 两者都有方法和/或属性(行为);
  • 两者都不应该具有非私有字段(数据);
  • 两者都不能直接实例化;
  • 从一个派生意味着实现它拥有的任何抽象方法,除非派生类型也是抽象的。

实际上,类和接口之间最重要的区别是:

  • 接口不能包含私有数据;
  • 接口成员不能具有访问修饰符(所有成员均为“公共”);
  • 一个类可以实现多个接口(与通常只能从一个基类继承的相反)。

由于类和接口之间唯一特别有意义的区别是围绕(a)私有数据和(b)类型层次结构-两者都与调用者没有什么区别-因此,通常不必知道类型是接口还是接口。一类。您当然不需要视觉指示。

但是,有一些特殊情况需要注意。特别是,如果您使用反射,拦截,动态代理/混合,字节码编织,代码生成或涉及直接与环境的打字系统或代码本身混淆的任何事情,那么这非常有帮助,有时甚至需要立即了解无论您是要处理接口还是类。您显然不希望您的代码神秘地失败,因为您试图添加一个类而不是接口作为mixin。

但是,对于典型的,普通的,通用的业务逻辑代码,不需要宣传抽象类和接口之间的区别,因为它们永远不会起作用。

综上所述,我还是倾向于给C#接口加上前缀,I因为这是Microsoft使用和提倡的.NET约定。当我向新开发人员解释编码约定时,仅使用Microsoft的规则就比解释我们为什么拥有自己的“特殊”规则要麻烦得多。


谢谢您的细分。为“类和接口之间的有意义的区别...” +1
Charles Sprayberry

23
+1表示“所有这些,无论如何,我总是倾向于为C#接口加上前缀,因为这是Microsoft使用和提倡的.NET约定”。这对于C#对我来说足够了。通过遵循此通用标准,其他.NET程序员更有可能识别该接口。
Robotsushi 2011年

4
作为同时编写Java和C#的人,陈述“所有这些,无论如何,我总是倾向于给C#接口加上前缀,因为这是Microsoft使用和提倡的.NET约定”-这是其中之一。帮助我记住我使用的语言,
Aidos

3
.NET(但不是Java)中的另一个区别是,许多.NET语言不允许接口包含静态方法,因此I从接口中砍掉可以为类保留与该接口关联的静态方法提供一个好名字(例如Enumerable<T>.EmptyComparer<T>.Default)。
2014年

我有一个问题。在C ++中,如果您打开一个标头并看到它class Foo已经扩展class Barclass Bar则在扩展或实现该标头时并不会立即显而易见。因此,现在您必须查看class Bar标题,以决定是否可以修改class Fooextend class Baz。此外,如果是否违反了“单个基类”,则I前缀提供了明显的视觉线索-因此,每次打开标头时,您都会进行健全性检查。
jsj

14

在许多方面,一致性比约定更重要。只要您在命名方案上保持一致,就不会很难使用它们。如果您愿意,前缀可以与I进行交互,或者只是保留名称不被修饰,只要您选择一种样式并坚持使用,对我来说就没有关系!


2
+1表示一致性>约定。在C#中,您将以I为前缀,而在Java中,则不会。不同的任务要求不同的约定
Bringer128

2
+1虽然我个人不希望界面上没有Is,但是与.Net项目中使用的BCL和第三方库不一致的做法并不是恕我直言。
jk。

13

这本书充满了好东西,但是我仍然会在接口名称上加上“ I”。

想象一下您有public interface ILog一个默认实现public class Log

现在,如果您决定拥有,则必须public interface Log突然将Log class更改为public class LogDefault或在这些行上进行更改。您从拥有一个额外角色变成了七个角色-这肯定是浪费。

在理论与实践之间通常存在细微的界限。从理论上讲,这是一个非常好的主意,但实际上却并非如此。



30
“想象一下,您具有带有默认实现公共类Log的公共接口ILog。” -那么您的默认实现名称不正确。怎么Log办?登录到控制台?要syslog?无处可去?这些应该叫ConsoleLogSyslogLogNullLog分别。Log对于具体的类不是一个好名字,因为它不是具体的名字。
塞巴斯蒂安·雷德尔

7
就我个人而言,这就是为什么我讨厌看到ITheClassName的原因,您只是无缘无故地创建了一个接口,仅仅是在TheClassName之上的干扰。如果您的界面有目的,那么应该可以给它一个比ITheClassName更好的名称
Richard Tingle,

类的名称只使用一次(实例化),接口的名称在所有地方都使用。在接口上添加字母以将字母保存在类名称上是错误的(字符)经济学。
sergut

@RichardTingle但是,我认为那里的常见用法很简单,所以MyClass具有可模拟的变化,对吗?也就是说,我们经常使用接口从本质上以允许测试的方式使公共方法真正成为公共方法。(不是说这是好还是坏,但只要了解了接口的动机往往是简单地让那些依赖容易mockable并解释1对1类MyClassIMyClass映射。)
鲁芬

8

好吧,这与实现接口或扩展类无关。在那种情况下,无论如何您都知道自己在做什么。

但是,当第三方代码(例如应用程序的另一个模块)对您的数据进行操作时,此代码不应在乎您是否提供接口或类。

这是抽象的全部要点。您正在向此第三方代码提供给定类型的对象。给定的类型具有一些可以调用的成员函数。够了

如果您的类型是接口或类是您的业务,而不是使用您的代码的人的业务。因此,您不应将您的代码详细信息泄露给该第三方代码。

顺便说一句,接口和类最后是引用类型。这才是重要的。因此,这是您的命名约定必须强调的内容。


@Mat>是的,那是我的错误,我对其进行了编辑。
deadalnix 2011年

5

大多数答案似乎都假定编程语言是Java或C#,其中存在接口/抽象类的概念(或关键字)。在这些情况下,在像样的IDE中,可以立即看到使用哪种类型的类进行交易,而编写public interface IMyClass则是不必要的重复。就像您不会写public final FinalMyClass-想象将每个关键字放在类名称中。

但是,在我看来,在C ++中,情况有些不同,因为必须深入头文件并查看该类是否具有任何虚拟方法来确定它是否是抽象类。在那种情况下,我可以清楚地看到写作的论点class AbstractMyClass

所以我的答案是:这取决于编程语言。

2016更新: 2年的C ++经验以后,我现在可能会认为在类名前面加上前缀是不好的做法Abstract,尽管我会说可能有些代码库过于复杂以至于没有道理。


您确定这可以回答问题吗?您可能希望阅读其他答案并阐明您的一些观点。
david.pfx 2014年

C ++肯定有接口,即使没有关键字也是如此。您怎么称呼每个成员函数都是纯虚函数且没有成员变量的类?

@ david.pfx:我想我确切地回答了“接口名称应以“ I”前缀开头吗?”这个问题:它取决于编程语言。如果您认为我的阐述不够清楚,那么我很乐意加以改进-哪些部分对您来说不明确?
Ela782 2014年

2
@JohnGaughan:确定有接口!但我的全部意义在于,它对关键字。C ++没有C ++,因此无论IDE /用户是否处理接口,它都对他/她可见。但是,在Java中,它是立即可见的。
Ela782 2014年

1
阅读评分最高的答案并进行比较。您是SO的新手,这是一个学习什么样的答案吸引选票的机会。就目前而言,您不会。通过更多的工作,更多的解释,更多的价值,它可能会。挂在那里!
david.pfx 2014年

4

遵循所使用语言的习惯用法。

每种语言都有自己的习惯用法和编码准则。例如,在C#中,惯用I前缀是接口的习惯。在Go中则不是。


+1遵循您所使用语言的习惯用法。显然,本书的作者是Java开发人员。.NET / C#:ILog Java:Log但是缺点:当我喜欢拥有一个实现ILog接口的Log类时.... MyLog,LogDefault,LogBase等...?丑陋,不是吗?更难看的是:LogImpl ....:D
hfrmobile

0

在我的(Java)代码中,我倾向于在公开给调用者的API中使用此约定:

  • 功能是通过接口提供的,而不是通过类提供的。
  • 非功能部件是类。

所谓非功能性,是指诸如纯数据结构(例如充当XML序列化或ORM的桥梁的类),枚举和异常之类的东西,所有这些都不可以是接口(好吧,您可以使用纯数据类,但这样做的工作量却很少,因为这些类除了保存数据没有其他作用)。

根据命名约定,接口倾向于映射到参与者名词(例如FooBarWatcher)或形容词(例如FooBarWatchable),并且纯数据类和枚举都映射到非活动名词(例如FooBarStatus);IDE可以指导API使用方,而无需特殊的命名约定。当然FooBarException,异常遵循通常的Java约定(FooBarError,,FooBarFault)。

我也经常将接口放在他们自己的包中,甚至放在他们自己的库中,只是为了确保我不愿意违反自己的规则。(当我从WSDL文档等外部资源获取代码时,这也有助于我管理构建。)


我也从未I在接口上添加前缀。作者当然这是一个接口!在API(或SPI)中!
Donal Fellows,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.