27

(在 .NET 的上下文中,它的价值是什么)

我倾向于不使用继承,也很少使用接口。我遇到了一个认为接口是自吐槽以来最好的东西的人。他到处使用它们。我不明白这一点,因此接下来的问题。我只是想检查一下我对接口的理解。

如果您在任何地方都使用界面,我假设您可以预测未来,您的应用程序需求已确定并且您的应用程序不会发生任何变化。对我来说,尤其是在早期开发过程中,界面成为了拖累。该应用程序在其生命周期中非常动态。如果您需要在界面中减去或添加成员,很多东西都会中断。上面的人说他创建了另一个接口来处理新成员。什么都没有。

不是组合吗?为什么不使用没有接口的组合?更灵活。

他如何处理必须从接口中减去成员的情况?基本上他不会。事情刚刚破裂,这很好,因为现在您可以看到所有受影响的区域并修复它们。我们不应该更优雅地找出所有相关代码路径的位置,而应该通过蛮力撕掉部分类?

我将软件应用程序视为图形。完整图是最坏的情况,具有 n(n-1)/2。这意味着每个班级都与每个班级交谈。令人困惑的蜘蛛网。n-1 是最好的,其中它们是严格的通信等级。添加另一个接口只是为了补偿一个新的需要的成员,会在图中添加一个顶点,这意味着更多的边和更强的 n(n-1)/2 方程的实现。没有接口的组合更像是 mixin。只有选择类使用特定的方法。使用接口,所有类都被迫使用成员,即使它们不需要它们。组合/混合方法不会添加新的不需要的边缘。

4

15 回答 15

40

接口不强制类使用方法。他们强制实现类来实现所有方法,但那是另一回事。

我喜欢接口将 API 从实现中分离出来的方式。诚然,这也可以通过访问修饰符完成,但接口使其更清晰。更重要的是,接口还使模拟变得更容易——这意味着您甚至可以在实现接口之前对依赖于接口的类进行单元测试。

是的,这意味着我经常会得到一个只有一个生产实现的接口。在我看来这不是问题,因为我已经获得了可测试性。

另一方面,我不会为每个类都写一个接口。我喜欢编写对象本质上提供服务的接口——身份验证、数据访问等。普通数据对象(即使具有重要行为)在接口、IME 方面没有那么有用。

于 2009-02-20T16:54:03.473 回答
7

根据维基百科

多态性在工业(面向对象编程理论)中的主要用途是属于不同类型的对象能够响应同名的方法、字段或属性调用,每个调用都根据适当的特定于类型的行为。程序员(和程序)不必事先知道对象的确切类型,因此确切的行为是在运行时确定的(这称为后期绑定或动态绑定)。

这就是接口如此有用的原因。

“上面的那个人说他创建了另一个接口来处理新成员。没有任何问题。”

我只是在这里猜测,但听起来这家伙来自旧学校的 COM 背景(当然,我可能是错的!)。想到我所编写的所有代码,我都会畏缩,因为我看到过这样的事情:

  • 小部件管理器
  • IWidgetManager2
  • IWidgetManager3

这不是使用接口的好方法。以我的经验,我看到了两个极端:害怕更改接口到每当添加新成员时创建新接口的地步,或者根本不使用接口并且拥有遭受高度耦合的产品。你需要找到一个很好的平衡点。改变界面并不总是世界末日。

您正在从事的项目的规模是多少?如果它是一个相对较小的项目,则很难看到接口的好处。另一方面,如果它是一个有几十万行代码并且由许多模块组成的项目,那么好处就会变得更加明显。

于 2009-02-20T16:54:11.663 回答
5

接口有许多有用的情况。当您需要将特定行为添加到可以在各种类中找到的类时,这就是接口的最佳时机。一个很好的例子是IDisposable界面——你有一些资源,当你完成后,需要及时离开。是数据库连接吗?是一些窗户把手吗?没关系。

另一个例子是当你真的知道它应该如何实现时,例如一个还不存在的对象的接口。也许该对象应该由您的库的客户端提供,或者必须由不受您控制的完全不同的模块实现。您基本上可以为类上可用的方法设计一个契约。

也就是说,我只在需要的地方使用它们。如果我可以用一个普通的类来做,或者如果它是特定对象固有的东西,我会把它变成一个类。正如其他人所说,为每个类使用接口有一些优点,但这是额外的开销,我看不到它有不错的净收益。大多数时候,我设计了我的类结构,使它们扁平而宽泛,尽可能少的依赖。

总而言之:如果您需要实现显着不同的通用功能,那么接口正是您所需要的。

于 2009-02-20T17:13:10.727 回答
3

接口帮助你保持对抽象的依赖。

使用接口的代码只依赖于接口,所以你知道对细节没有人为的依赖。这为您在将来更改代码方面提供了很大的自由,因为您确切地知道在“修复”错误或重构时应该和不应该破坏什么。

在我看来,这是好的代码设计的本质。

于 2009-02-23T20:51:46.137 回答
2

“上面的人说他创建了另一个接口来处理新成员。没有什么问题。

不是组合吗?为什么不使用没有接口的组合?更灵活。”

您似乎在考虑继承方面的组合,例如,“我会将所有这些功能继承到这个对象中来完成这项工作。” 这是编写代码的糟糕方式。这就像在说,“我想建造一座摩天大楼,所以我会学习所有需要知道的工作,从如何创建蓝图到如何打地基和安装照明......”

考虑组合,而不是根据每个执行单个任务的单独对象。为了完成一项复杂的工作,我现在可以依靠这些单独的对象来执行它们各自的任务。这就像雇用建筑师和施工人员来建造摩天大楼。我不需要非常详细地知道他们每个人是如何工作的,只要他们能做到就行。在代码中,这意味着将依赖项注入对象以执行复杂的任务,而不是继承。

那么接口适合哪里呢?它们是各个对象之间的契约。它们使您能够不关心个别的具体实现。它们允许您与一堆共享相同职责但实际上可能具有不同实现的对象说共同语言。接口成为执行任务的对象的抽象。我不需要知道每个拿着锤子的人是如何工作的,只要他知道如何使用 HitNail()。

当你开始用许多具有单一职责的小类来编写复杂的代码系统时,你很快就会发现,如果你过分依赖给定类的具体实现,你可能会遇到严重的问题,并且该类开始发生变化...因此,我们不依赖具体的实现,而是创建一个接口 - 一个抽象。我们说,“我不在乎你如何做 JobX,只在乎你做。”

接口还有其他好处,如测试、模拟等……但这些并不是为接口编码的原因。原因是创建一个不依赖于细节的代码系统,因此彼此高度耦合。这就是为什么用你的图形思维大脑,你应该害怕将一堆具体的类相互耦合。因为其中一个类的变化会引起连锁反应。

当你把自己耦合到一个抽象而不是一个具体的类时,你就限制了耦合。你说,我只会和我们都同意的合同耦合,其他什么都没有。如果实现该契约的类改变了其完成任务的内部方式,您不必在意,因为您不依赖于非契约属性或方法。你只依赖于商定的合同。

于 2009-02-20T17:48:35.603 回答
2

如果您想了解何时使用接口,以及它们的用途是什么,我认为您应该看看这本书:Head First Desing Patterns

这本书真正帮助我理解了接口的魅力所在。

在读这本书之前,我知道什么是界面,但我完全不知道什么时候应该使用它们。

于 2009-02-20T17:54:29.303 回答
2

它是关于依赖关系的。您希望您的代码在适当的情况下依赖于接口类型而不是具体类型。例如,您可能有多个具体类型,它们都执行类似的行为,但实现方式不同。您是否希望您的代码库依赖于这些多种具体类型或一个接口?当您需要进行更改时,您认为您的代码库有多灵活?

对于您不想通过使用组合来使用公共成员的观点,我想说您只是封装了不必要的依赖项。如果您想使用组合,那么您可以通过组合接口类型而不是具体类型来大大减少依赖关系。

要获得更好的解释,请尝试查看有关控制反转的文献。

于 2009-02-21T15:56:50.717 回答
1

在依赖注入和 IoC 框架的上下文中,接口对我来说最有意义。我喜欢您可以定义一组行为(方法)并通过实现接口“保证”这些行为的想法。现在,您可以通过单个程序集和配置文件更新将全新功能插入现有系统。

界面的有效设计确实需要大量的前瞻性规划,我发现它们在大型系统和框架的上下文中最有用。当它们有用时,它们真的很有用。我最喜欢的几个:

  • IComparable(您决定您的对象如何相互比较)
  • IQueryable(LINQ有人吗?)
  • IDisposable(把你的using陈述放在手边)
于 2009-02-20T17:05:48.657 回答
0

我认为您描述了自上而下的设计方法。
您不必这样做,但有时他确实会提供帮助。

于 2009-02-20T17:20:26.217 回答
0

我会参考 .net 设计指南:http: //msdn.microsoft.com/en-us/library/ms229013.aspx

使用接口的原因有很多,但我也发现它们经常因为错误的原因而被过度使用。接口在处理值类型时提供了更大的灵活性,并且在处理集合等时非常有用,但是在为我自己的项目设计类层次结构时,我总是尝试首先考虑简单性,并且接口通常会导致(不必要的)更复杂的情况。

我的经验法则是实现每个有意义的 BCL 接口,并且只有在我自己的接口真正提供了非常有价值的东西时才将其添加到我的设计中。与其拥有 IWidgetManager、IWidgetManager2 等...我更愿意拥有抽象类 WidgetManager 并根据需要实现具体方法。

于 2009-02-21T02:15:21.017 回答
0

这个人听起来像是误解了对接口编程的概念。它不是指仅使用interface.Net/Java 中的关键字进行编程,也不是指仅使用 C++ 中的纯虚函数的类,该原则中所指的接口是封装了低级接口的高级构造的一个系统。与乘法一样,它封装了将数字添加到自身特定数量的重复的想法。但是当我们说 3 * 2 时,我们不在乎缓存括号中的部分是 3 + 3、2 + 2 + 2 还是 (2 + 2) + 2。只要我们从它那里收到 6。

事实上,接口的概念可以由这些中的一个或一个组合来填充,interface就像abstract class许多 GoF 模式的情况一样。只是interface关键字模糊了水。

这很有趣。这家伙的想法可能是产生评论的原因,例如围绕 StackOverflow 第 38 集的评论。

于 2009-02-23T20:36:28.343 回答
0

听起来你的朋友严重误用了界面。

我还看到到处都有界面的网络应用程序。有些人似乎认为接口比普通的方法签名更好。简单的参数已经为我们提供了一个契约——在大多数情况下,我相信这已经足够了,并且可以简化代码。

在我看来,接口在 IDisposable 或 ICollection 等情况下最有用——它们指向对象期望的一组特定功能。在这些情况下,它们似乎是完成这项工作的正确工具。

于 2009-02-27T06:54:18.100 回答
0

我们在自定义 UI 元素上大量使用接口,每个元素都期望某种方法存在于另一个元素上(并且接口强制存在)。

于 2009-02-27T07:01:16.600 回答
0

接口的主要用途是允许实现的变化,允许在运行时切换代码。这样做有很多原因/理由。

过去有些人认为系统中的每个类都应该有一个接口,但这被广泛认为是矫枉过正。现在使用接口,其中类的实例可以作为系统操作的一部分进行更改(表示状态):诸如 Strategy 和 Command 之类的 GoF 模式捕获了这种使用,和/或需要替换系统的某些部分以进行测试(即依赖项)注射)。如果开发人员遵循测试驱动的开发实践,关键基础设施对象将具有接口。这允许测试对这些对象进行“模拟”,以测试系统的控制流。接口的这种使用与 Liskov Substitution Principle(OO 设计的原则之一)之间存在关系

不太关心调用者可用的接​​口的另一个用途是标记接口。这是一种将元数据与类定义相关联的方法。这可能会影响系统查看对象的方式(例如允许对其进行序列化),或者它可能只是用作文档。

于 2009-06-04T20:16:11.607 回答
0

使用接口的最重要原因之一是它们允许我们为类编写单元测试并传入我们自己的依赖项。通过将依赖项放在接口后面,我们在应用程序代码中打开了“Seams”,可以轻松编写单元测试。我在很多答案中都没有看到这个测试角度,但是了解没有接口,这些依赖项(例如 Web 服务或文件系统引用)可能变得非常难以测试或充其量是非常重要的是相当有条件的。我在这里写了一篇文章:http: //jeffronay.com/why-use-an-interface/更详细的代码示例显示了一个没有接口的服务类,然后使用接口重新编写了同一个类,以展示使用接口时的测试灵活性。

于 2017-12-27T00:12:52.607 回答