何时需要接口?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了何时需要接口?相关的知识,希望对你有一定的参考价值。
(在.NET的背景下,它的价值)
我倾向于不使用继承,很少使用接口。我遇到了一个认为接口是自吐以来最好的东西的人。他到处使用它们。我不明白这一点,因此接下来的问题。我只想检查一下我对接口的理解。
如果您在任何地方使用接口,我假设您可以预测未来,您的应用程序要求已经确定,并且应用程序中的任何内容都不会发生变化。对我来说,特别是在早期开发过程中,界面变得拖累。该应用程序在其生命中非常有活力。如果你需要减去或添加成员到界面,很多东西都会破坏。上面的人说他创建了另一个界面来处理新成员。什么都没有打破。
那不是那个构图吗?为什么不使用没有接口的组合?更灵活。
他如何处理必须从界面中减去成员的情况?基本上他没有。事情刚刚破裂,这很好,因为现在你可以看到所有受影响的区域并修复它们。我们应该通过暴力破解掉部分类,而不是更优雅地找出所有相关代码路径的位置?
我认为软件应用程序是一个图形。完整的图表是最坏的情况,具有n(n-1)/ 2。这意味着每个班级都与每个班级交谈。令人困惑的蜘蛛网。 n-1是最好的,它们是严格的沟通等级。添加另一个接口只是为了补偿一个新的需要成员,为图形添加一个vertici,这意味着更多的边缘和更强的n(n-1)/ 2方程的实现。没有接口的组合更像是mixins。只有选择类使用特定方法。使用接口,所有类都被迫使用成员,即使他们不需要它们。组合/混合方法不会添加新的不需要的边。
接口不强制类使用方法。他们强制实现类来实现所有方法,但这是另一回事。
我喜欢接口将API与实现分开的方式。不可否认,这也是通过访问修饰符完成的,但接口使其更清晰。更重要的是,接口也使得模拟更容易 - 这意味着您甚至可以在实现之前对依赖于接口的类进行单元测试。
是的,这意味着我经常会得到一个只有一个生产实现的界面。在我看来,这不是问题,因为我已经获得了可测试性。
另一方面,我没有为每个类编写接口。我喜欢编写一个对象基本上提供服务的接口 - 身份验证,数据访问等。简单数据对象(即使具有重要行为)在接口IME方面也不那么有用。
我将参考.net设计指南:http://msdn.microsoft.com/en-us/library/ms229013.aspx
使用接口有很多原因,但我也发现它们经常因为错误的原因而被过度使用。在处理值类型时,接口提供了更大的灵活性,并且在处理集合等时非常有用,但在为我自己的项目设计类层次结构时,我总是首先考虑简单性,并且接口经常导致(不必要)更复杂的情况。
我的经验法则是实现每个有意义的BCL接口,并且只在实际提供非常有价值的东西时才将我自己的接口添加到我的设计中。而不是拥有IWidgetManager,IWidgetManager2等...我宁愿拥有抽象类WidgetManager并根据需要实现具体方法。
这个人听起来像是误解了编程到界面的概念。它不是指在.Net / Java或类中只使用interface
关键字的编程,而是在C ++中只有纯虚函数,在该原则中引用的接口是封装低级的高级构造。一个系统。与乘法一样,它封装了为自身添加一个特定数量的重复的想法。但是当我们说3 * 2时,我们不在乎它是3 + 3,2 + 2 + 2还是(2 + 2)+ 2,其中括号中的部分被缓存。只要我们收到一个6。
事实上,接口的概念可以用interface
或abstract class
或它们的组合来填充,就像许多GoF模式的情况一样。只是interface
关键词有点含糊不清的水。
这很有趣。这个人的想法可能就是那些以StackOverflow第38集为中心的评论。
听起来你的朋友严重误用了界面。
我还看到了遍布各处的界面的Web应用程序。有些人似乎认为界面在某种程度上比常规方法签名更好。简单的参数已经为我们提供了契约 - 在大多数情况下,我认为这已经足够了,并且可以使代码更简单。
在我看来,接口在IDisposable或ICollection等情况下最有用 - 它们指向对象所期望的某组功能。在这些情况下,它们似乎是适合这项工作的工具。
我们在自定义UI元素上大量使用接口,其中每个元素都期望某个方法存在于另一个元素上(并且接口强制存在)。
接口的主要用途是允许实现的变化,允许在运行时切换代码。这样做有很多原因/理由。
在过去,有人认为系统中的每个类都应该有一个接口,但这被广泛认为是过度杀伤。现在使用接口,其中类的实例可以作为系统操作的一部分进行更改(表示状态):策略和命令等GoF模式捕获此用途,和/或系统的某些部分需要替换以进行测试(即依赖性)注射)。如果开发人员遵循测试驱动的开发实践,则关键基础结构对象将具有接口。这允许测试对这些对象进行“模拟”以测试系统的控制流。这种界面的使用与Liskov替代原则(OO设计的原则之一)之间存在关联
接口的进一步用途是标记接口,其更少关注呼叫者可用的内容。这是一种将元数据与类定义相关联的方法。这可能会影响系统查看对象的方式(例如,允许它进行序列化),或者它可能仅用作文档。
使用接口的最重要原因之一是它们允许我们为类编写单元测试并传入我们自己的依赖项。通过将依赖项置于接口后面,我们在应用程序代码中打开了“Seams”,可以轻松编写单元测试。我没有看到许多答案中提到的这个测试角度,但是理解没有接口,这些依赖关系(例如Web服务或文件系统参考)可能变得非常难以测试或者最多非常有条件的。我在这里写了一篇文章:http://jeffronay.com/why-use-an-interface/,它通过显示没有接口的Service类的代码示例进行更详细的介绍,然后使用接口重新编写相同的类来演示使用接口时的测试灵活性。
根据wikipedia,
工业中多态性的主要用法(面向对象编程理论)是属于不同类型的对象响应同名的方法,字段或属性调用的能力,每个调用根据适当的类型特定行为。程序员(和程序)不必事先知道对象的确切类型,因此确切的行为是在运行时确定的(这称为后期绑定或动态绑定)。
这就是使接口如此有用的原因。
“上面的人说他创建了另一个界面来处理新成员。没有什么能打破。”
我只是在这里猜测,但听起来这个家伙来自旧学校的COM背景(当然,我可能是错的!)。让我感到畏缩,想到我曾经在我看过这样的事情的所有代码:
- IWidgetManager
- IWidgetManager2
- IWidgetManager3
这不是使用接口的好方法。根据我的经验,我已经看到了两个极端:害怕将接口更改为每当添加新成员时创建新接口,或者根本不使用接口并且产品遭受高度coupling。你需要找到一个很好的平衡点。改变界面并不总是世界末日。
您正在进行的项目规模是多少?如果它是一个规模相对较小的项目,很难看到接口的好处。另一方面,如果它是一个包含数十万行代码并且由许多模块组成的项目,那么其好处就会变得更加明显。
接口有许多有用的情况。当您需要向可以在各种类中找到的类添加特定行为时,这是接口的最佳时间。一个很好的例子是IDisposable
界面 - 你有一些资源,当你完成后,需要及时消失。它是数据库连接吗?它是一些窗口处理?无所谓。
另一个例子是当你真的不知道应该如何实现它时,例如一个尚不存在的对象的接口。也许该对象应该由您的库的客户提供,或者必须由不受您控制的完全不同的模块实现。您基本上可以为类中可用的方法设计合同。
也就是说,我只在需要的地方使用它们。如果我可以使用常规类,或者如果它是特定对象固有的东西,我将使它成为一个类。正如其他人所说的那样,为每个类使用接口有一些优点,但这是一个额外的开销,我没有看到它有不错的净收益。大多数时候,我设计了我的类结构,使它们平坦而宽阔,并且尽可能少地依赖。
总结:如果您需要的通用功能有很大不同,那么接口就是您所需要的。
接口可以帮助您保持对抽象的依赖性。
使用接口的代码仅取决于接口,因此您知道细节上没有人为依赖性。这为您在未来更改代码提供了很多自由,因为您确切知道在修复错误或重构时应该和不应该破坏的内容。
在我看来,这是良好的代码设计的本质。
“上面的人说他创建了另一个界面来处理新成员。没有什么打破。
那不是那个构图吗?为什么不使用没有接口的组合?更灵活。“
你似乎在考虑继承方面的构成,例如,“我会将所有这些功能继承到这个对象中来完成这项工作。”这是编写代码的坏方法。这就好说,“我想要建造一座摩天大楼,所以我将学习每一项工作,从如何创建蓝图到如何使基础变差,安装照明......”
根据每个执行单个任务的单独对象来考虑组合。为了完成复杂的工作,我现在可以依靠这些单独的对象来执行各自的任务。这就像雇用建筑师和施工人员来建造摩天大楼一样。我不需要详细了解他们每个人的工作方式,只要他们能做到。在代码中,这意味着将依赖项注入对象以执行复杂任务而不是继承。
那么接口适合哪里?它们是各个对象之间的契约。它们使您能够不关心个别的具体实现。它们允许您与一堆具有相同责任的对象说一种通用语言,但实际上可能有不同的实现。该接口成为执行任务的对象的抽象。我不需要知道每个带锤子的人是如何工作的,只是他知道如何使用HitNail()。
当你开始用许多具有单一职责的小类组成复杂的代码系统时,如果你过分依赖给定类的具体实现,那么你很快就会发现你可能会遇到严重的问题,并且这个类开始改变...因此,我们不是依赖于具体的实现,而是创建一个接口 - 一个抽象。而且我们说,“我不关心你是怎么做JobX的,只有你这样做。”
测试,模拟等接口还有其他好处......但这些并不是编写接口的原因。原因是创建一个代码系统,该系统不依赖于细节,因此彼此高度耦合。这就是为什么,你的图形思维大脑,你应该害怕将一堆具体的类相互耦合。因为其中一个类的更改会导致连锁反应。
当你把自己耦合到一个抽象而不是一个具体的类时,你限制了耦合。你说,我只会加入我们都同意的合同,而不是别的。“如果实现该合同的类改变了其完成任务的内部方式,则您不关心,因为您不依赖于非合同属性或方法。你只依赖于商定的合同。
如果你想了解何时使用接口,以及它们的用途,我认为你应该看看这本书:Head First Desing Patterns。
这本书真的帮助我理解了界面的优点。
在阅读本书之前,我知道界面是什么,但我完全不知道何时应该使用它们。
它是关于依赖性的。您希望代码依赖于接口类型而不是适当的具体类型。例如,您可能有多个具体类型,它们都执行类似的行为,但实现方式不同。您是否希望您的代码库依赖于这些多个具体类型或一个接口?当您需要进行更改时,您认为您的代码库有多灵活?
至于你不想通过使用组合使用公共成员,我会说你只是封装了不必要的依赖。如果要使用合成,则可以通过组合接口类型而不是具体类型来大大减少依赖性。
有关更好的解释,请尝试查看有关inversion of control的文献。
在依赖注入和IoC框架的上下文中,接口对我来说最有意义。我喜欢你可以定义一组行为(方法)并通过实现接口来“保证”这些行为的想法。现在,您可以使用单个程序集和配置文件更新将全新功能插入现有系统。
有效的接口设计确实需要大量的前瞻性规划,我发现它们在大型系统和框架的环境中最有用。当它们有用时,它们真的很有用。我最喜欢的一些:
IComparable
(你决定你的对象如何相互比较)IQueryable
(LINQ任何人?)IDisposable
(保持你的using
声明方便)
我认为你描述了top-down design方法。 你不必这样做,但有时他真的很有帮助。
以上是关于何时需要接口?的主要内容,如果未能解决你的问题,请参考以下文章
如何确定 Fragment 何时在 ViewPager 中可见
此应用小部件片段中所有意图 (PendingIntents) 的逻辑流