32

这种命名约定背后的基本原理是什么?

我没有看到任何好处。额外的前缀只会污染 API。

我的想法与康拉德对这个相关问题的回答一致的;选择的答案主要是我在这里要求的。

4

18 回答 18

54

完全相反,命名约定清楚地标识了一个接口。

例如,如果您有:

public class Dog : IPet, IMammal
{
    ....

仅仅通过阅读它,我可以有把握地假设 IPet 和 IMammal 可能是接口。

.NET CLR 允许单类继承。所以,如果我有一个基类..我只能从它继承一个类。让我们将 IPet 接口更改为基类..我们的示例现在变为

public class Dog : Pet, IMammal
{
    ....

我从 Pet 类继承并实现 IMammal 接口。

如果我们按照您的建议执行并删除了字母“I”,我们将得到:

public class Dog : Pet, Mammal
{
    ....

我从哪个类继承?我正在实现哪个接口?它会变得混乱吗?(仅供参考..您应该始终将基类放在首位,因此您可以争论这一点...但是如果您主张从接口名称前缀中删除字母 I,我怀疑您是否也遵循了这种做法)

正如您所看到的,命名约定很容易告诉我很多关于我的对象的信息,而无需我进一步调查。我可以很容易地看到我正在继承什么与我正在实施什么。

于 2009-01-13T01:13:22.230 回答
31

我也喜欢它,因为我可以将它读作“IcanSave”或“IDoDoubleEntry”等中的“I verb-behavior”……

于 2009-01-13T01:11:28.333 回答
26

我认为 IInterface 命名约定很愚蠢。这是匈牙利符号的一个例子,我赞同鄙视匈牙利符号的思想流派。如果您的接口只有一个具有相同名称的实现,请考虑这是代码异味的可能性。

但是,我仍然使用它,因为在这种情况下,IInterface 是微软推荐的,“标准胜过更好”。

于 2009-01-16T17:39:04.560 回答
17

为什么这不是句法突出显示而不是匈牙利符号的功能?如果区分类和接口非常重要,为什么 IDE 不将引用接口的标识符用斜体表示。我讨厌在字段之前放置“ ”或“m ”,在类之前放置“C”等等。更糟糕的是,它鼓励程序员编写非常糟糕的 API,例如:

public class List : IList

而不是更合理的:

public class LinkedList : List
public class ArrayList : List
public class HashList : List

甚至 .NET 通用类的作者也落入了这个陷阱。类名永远不应该是只删除“I”的接口名称。类名应始终告诉用户该类与接口的其他可能实现有何不同。仅出于这个原因,我投票赞成放弃愚蠢的“我”。

另外,当我使用智能感知时,我想按功能区域对事物进行分组,而不是按类还是接口。我从来没有想过,“我需要一个接口,而不是一个类。” 我总是在想,“我需要做 X 的东西”。

于 2010-10-12T18:11:59.437 回答
11

实际上,我发现避免命名冲突很有用,例如,我可能会创建一个名为 Fred 的具体类来实现 IFred

于 2009-01-13T01:29:23.150 回答
5

我一直认为将动词用于行为界面很有趣。这与使用名词的类命名约定背道而驰,但它允许类“说出”它的行为。

class Dog: IBark

这对于 WCF 接口之类的结构化接口效果不佳,但我们不需要一直玩得开心。

要回答您的问题,请将其I视为“实现”所以...

class DogDataService : Dog, IDataService

该服务类继承自Dog并实现IDataService

我仍然没有真正回答你的问题,但I它很有用,因为你会在命名空间、类和接口之间遇到命名冲突。

namespace DataService
interface DataService
class DataService: DataService

所以我们最终得到

namespace DataServices
interface IDataService
class DataService : IDataService

我认为实际上,这是一个理智的约定。

于 2009-10-30T16:43:41.020 回答
4

如果您考虑两个“最佳实践格言”

清晰为王

噪音不好

这些之间存在冲突。问题是:什么时候清晰变成噪音?

对我来说,写起来Person person = new PersonImpl()IPerson person = new Person().

于 2011-12-23T12:02:09.163 回答
3

“I”约定似乎是一个与今天无关的旧约定。当前的代码编辑器提供了有关您正在使用的类型的大量见解,因此认为更容易识别接口就像要求命名空间以“N”为前缀,因为您想确保不会混淆它一个具体的类(带有“C”的前缀?)。

约定并不意味着它是一个好的约定。有时候,这只是因为人们开始使用它......

以 C# 文档生成器为例:它不关心它...如果您的界面没有以“I”为前缀,您仍然会在文档的界面部分看到您的界面。您真的认为在文档的接口部分中为所有接口添加前缀“I”是相关信息并帮助您更好地识别接口吗?

于 2010-09-03T23:03:28.763 回答
3

要么就是这样,要么在接口的实现中添加“Impl”(啊)。我对“我”没有意见,它是对接口的最简单直接的命名。

于 2009-01-13T01:05:07.053 回答
3

区分接口和类的需要实际上表明存在设计缺陷。在设计良好的应用程序中,它总是很清楚。一个子类应该始终是一个专业化,并且类只能专注于一个学科,永远不会更多。

一个类应该有一个存在的理由。永远不应要求将次要角色放在基类中。例如:

public class XmlConfigurationFile : ConfigurationFile, IDisposable
{
}

public class YamlConfigurationFile : ConfigurationFile, IDisposable
{
}

第一个是专门针对 Xml 的配置文件,第二个是专门针对 Yaml 的配置文件。这些也是一次性的,但这并不重要。由于处理过程不同,您没有创建这两个类。

将此与以下内容进行对比:

public class XmlConfigurationFile : IDisposable, ConfigurationFile
{
}

这将告诉您 XmlConfigurationFile 的主要目的是它是一次性的。您可以将其用作表示配置文件的一种方式,这很好,但只是次要的。

当您创建具有多种存在原因的类时,问题就开始了:

public class MyConfigurationFile : XmlConfigurationFile, YamlConfigurationFile
{
}

即使 XmlConfigurationFile 和 YamlConfigurationFile 本来是接口,它仍然表明设计不好。你的配置文件怎么能同时是xml和yaml呢?

如果您通读给出的示例(此处和其他地方),人们总是很难找到 I 前缀何时重要的好示例。这里的答案之一是:

public class Dog : Pet, Mammal
{
}

这就是这个类在一个关于宠物的应用程序中的样子。狗的主要目的是成为专门的宠物,可以做与宠物相关的事情,而不是哺乳动物。

public class Dog : Mammal, Pet
{
}

这就是同一类在有关动物分类的应用程序中的外观。很高兴知道狗是宠物,但它的主要目的是成为一种专门的哺乳动物,可以做与哺乳动物相关的事情。

我认为您的课程应该告诉您有关应用程序架构和领域的正确故事。要求接口以“I”为前缀是一项技术要求,并不能帮助您更好地讲述应用程序的故事。

一旦你开始编写小型的、专用的、单一用途的类,了解它是实现还是扩展的需求将自动消失。

于 2016-08-05T12:13:08.733 回答
2

它使其易于识别为接口。

于 2009-01-13T01:05:02.810 回答
1

命名约定的好处是在您使用对象之前告诉您一些有关该对象的信息。命名约定已被广泛使用多年,一直追溯到 fortran 坚持整数值被限制(如果我没记错的话)为变量名称,如“i”和“j”。

匈牙利符号将命名约定提升到了一个全新的丑陋水平,它描述了变量类型,无论它是否是指针等。我们中的许多人接触到大量使用匈牙利符号的代码时都会出现紧张的抽搐和口吃。

使用 I 为接口名称添加前缀是一种识别该对象的相对低影响、无害的方式。

于 2009-01-13T01:12:15.947 回答
1

TL;DR - 提取interface IFoo自在class FooSOLID 解耦中很常见,尤其是用于单元测试目的

对我来说,类Foo实现接口的双重约定IFoo(特别是如果两者都在同一个程序集中)传达了一个特定的意图:

  • 将依赖项耦合到 aFoo应该始终是间接的,通过相应的IFoo接口(并且可能通过 IoC 容器注入)
  • 的初始设计IFoo是一个专有的、不可重用的接口,专门允许依赖的类Foo在单元测试期间模拟这种依赖关系。
  • 除此之外,读者不需要在IFoo界面设计中推断出任何额外的智能
  • 相反,如果稍后需要多个具体的实现类,IFoo则需要将适当的接口隔离设计改进到层次结构中。

基本原理

为了能够模拟或存根一个类,单元测试中广泛接受的最佳实践是仅通过接口解耦类之间的依赖关系。这种接口解耦也将用于类,否则这些类将永远不会对多态性有设计要求(即,如果没有单元测试的需要,只会存在一个这样的实现)。

因此,这些接口的重构和重用(例如 的接口隔离原则SOLID)并不经常应用于此类“可模拟”接口 - ' 的公共方法、属性和事件之间通常存在 1:1 的相关性mockable' 类 ( Foo) 及其解耦接口IFoo(类似于VB 中 COM 时代的自动接口)。

诸如VS和 Resharper 之类的工具可以使从类中提取此类公共符号到单独的接口中变得微不足道,作为事后的想法。

此外,如果我们考虑像 Moq 这样的 Mocking 框架允许动态定义接口的实现,我们就不必浪费精力来命名具体的测试双重实现类。

于 2015-11-14T08:21:36.533 回答
0

这只是一个命名约定,所以每个人都会知道它是接口还是其他东西,它不是强制性的,也不是编译器或 IDE 的,但我一生中看到的所有接口都以字母 I 开头

于 2010-04-30T00:20:15.253 回答
0

首先,我认为前缀 I then description 是错误的,因为这意味着实现可以有一个更短的名称。IList (intf) -> 列表。这是一种反模式,因为我们都知道我们应该在创建时使用 intf 并且可能只使用具体类型。不要激怒我,这是一个概括,但前提是 intf 很少 impl 。实现名称应该描述它如何实现 intf 或它在做什么。想想intf List,LinkedList,它使用链表实现List。谁在乎它是否更长,因为我们大部分时间都应该使用 List 。如果我们有一个实现许多 intf 的类,我们可能不应该包含所有 intf 作为类的真正目的的影子。在没有 intf 的情况下删除的东西是有意义的。例如,人们叫我的名字而不是人,兄弟,使用我的名字的开发人员等是最好的最具描述性的名字。我想如果一个类是一个简单的 intf 然后调用它 Default Intf 这使得它在 ious 这是 Intf 的默认实现。类的名称最终应该是人类可读的,并且几乎是描述其目的的简短短语。前缀代码等并不是很好,因为我们用文字而不是代码进行交流。计算机并不关心类的名称,所以剩下的就是我们为事物命名,以便这些名称对我们和我们的同事有所帮助。

于 2010-04-30T00:42:19.820 回答
0

我似乎是匈牙利符号的传统约定。 接口命名指南说“在接口名称前加上字母 I,表示该类型是一个接口。” 框架设计指南还说“DO 在接口名称前加上字母 I,表示该类型是一个接口。”

这只是一个编码约定,因此很难确定好坏。重要的是一致性。

于 2013-11-29T03:31:16.093 回答
-1

最有可能让它在智能感知中易于识别,因为所有接口都会聚集在一起。类似于我如何在我的所有 UI 控件前加上 btn、tb、lb。当智能感知启动时,所有东西都聚集在一个简单的组中。

于 2009-01-13T01:10:11.067 回答
-1

有了关于命名约定的所有争论,并为变量和方法赋予正确的名称,这些变量和方法实际上描述了它们的作用......为什么不只是命名你的接口(例如 PetInterface、PlayerInterface 等)并取消前缀“I”所有一起。所以你必须输入额外的 9 个字母,至少“I”被删除,我们知道它不是一个类,因为它说“接口”。

于 2014-10-27T16:48:12.110 回答