在我的开发学习中,我觉得我必须学习更多关于接口的知识。

我经常读到它们,但我似乎无法理解它们。

我读过这样的例子:动物基类,IAnimal接口的东西,如“Walk”,“Run”,“GetLegs”等-但我从来没有工作过,觉得“嘿,我应该在这里使用接口!”

我错过了什么?为什么这个概念对我来说这么难理解!我只是害怕这样一个事实,我可能从来没有意识到一个具体的需要-主要是由于一些缺失的理解他们!这让我觉得我作为一名开发人员缺少了一些东西!如果有人有过这样的经历,并取得了突破,我会很感激一些关于如何理解这个概念的建议。谢谢你!


当前回答

In my experience the driving force to create interfaces didn't occur until I start doing unit testing with a mocking framework. It became abundantly clear that using interfaces was going to make mocking much easier (since the framework depended on the methods being virtual). Once I started I saw the value of abstracting away the interface to my class from the implementation. Even if I don't create an actual interface, I try now to make my methods virtual (providing an implicit interface that can be overridden).

我发现还有许多其他原因可以加强重构到接口的良好实践,但是单元测试/模拟的事情提供了最初的“顿悟时刻”的实践经验。

EDIT: To clarify, with unit testing and mocking I always have two implementations -- the real, concrete implementation and an alternate mock implementation used in testing. Once you have two implementations, the value of the interface becomes obvious -- deal with it in terms of the interface so you can replace the implementation at any time. In this case I'm replacing it with a mock interface. I know that I can do this without an actual interface if my class is constructed properly, but using an actual interface reinforces this and makes it cleaner (clearer to the reader). Without this impetus, I don't think I would have appreciated the value of interfaces since most of my classes only, ever have a single concrete implementation.

其他回答

把接口想象成一个契约。这是一种说法,“这些类应该遵循这些规则。”

所以在IAnimal的例子中,它是一种说,“我必须能够在实现IAnimal的类上调用Run, Walk等。”

为什么这个有用?您可能希望构建一个函数,该函数依赖于必须能够在对象上调用Run和Walk这一事实。你可以有以下内容:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

... 对所有你知道能跑能走的物体重复这一步骤。然而,在你的IAnimal接口中,你可以像下面这样定义函数:

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

通过根据接口编程,您实际上是信任类来实现接口的目的。所以在我们的例子中,想法是“我不在乎他们怎么跑和走,只要他们能跑和走。”只要他们履行协议,我的RunThenWalk就有效。它在不了解任何其他课程的情况下运行得很好。”

在这个相关的问题上也有很好的讨论。

一些非编程示例可能帮助您了解接口在编程中的适当使用。

There's an interface between electrical devices and the electricity network - it's the set of conventions about the shape of the plugs and sockets and the voltages/currents across them. If you want to implement a new electrical device, as long as your plug follows the rules it will be able to get services from the network. This makes extensibility very easy and removes or lowers the costs of coordination: you don't have to notify the electricity provider about how your new device works and come to a separate agreement about how to plug your new device into the network.

各国都有标准的铁路轨距。这使得铺设铁轨的工程公司和建造在这些铁轨上运行的列车的工程公司之间实现了分工,并使铁路公司有可能在不重新设计整个系统的情况下更换和升级列车。

The service a business presents to a client can be described as an interface: a well defined interface emphasises the service and hides the means. When you put a letter in a mailbox, you expect the postal system to deliver the letter within a given time but you have no expectations about how the letter is delivered: you don't need to know, and the postal service has the flexibility to choose the means of delivery that best meets the requirements and current circumstances. An exception to this is the ability of customers to choose airmail - that's not the kind of interface a modern computer programmer would have designed, since it reveals too much of the implementation.

来自自然的例子:我不太喜欢eats(), makesSound(), moves()等例子。它们确实描述了行为,这是正确的,但它们没有描述交互以及它们是如何被启用的。在自然界中,使相互作用成为可能的界面的一个明显的例子是与繁殖有关的,例如一朵花为蜜蜂提供了一个特定的界面,以便授粉能够发生。

作为一个。net开发人员,你完全有可能一辈子都不编写自己的接口。毕竟,没有它们,我们也活了几十年,我们的语言仍然是图灵完备的。

我不能告诉你为什么你需要接口,但我可以给你一个我们在当前项目中使用它们的列表:

在我们的插件模型中,我们通过接口加载插件,并将该接口提供给插件编写者以使其遵循。 在我们的机间消息传递系统中,消息类都实现了一个特定的接口,并使用该接口“解包装”。 我们的配置管理系统定义了一个用于设置和检索配置设置的接口。 我们使用一个接口来避免讨厌的循环引用问题。(如果没有必要,就不要这样做。)

我想如果有一个规则,那就是当你想在一个is-a关系中对几个类进行分组,但你不想在基类中提供任何实现时使用接口。

一旦你需要为你的类强制一个行为,你应该定义一个接口。

动物的行为可能包括走、吃、跑等。因此,您将它们定义为接口。

另一个实际的例子是ActionListener(或Runnable)接口。您可以在需要跟踪特定事件时实现它们。因此,您需要在类(或子类)中提供actionPerformed(Event e)方法的实现。类似地,对于Runnable接口,提供公共void run()方法的实现。

此外,您可以让任意数量的类实现这些接口。

使用接口(在Java中)的另一个实例是实现c++中提供的多重继承。

当您成为库开发人员(为其他编码员编写代码的人)时,接口将变得很明显。我们中的大多数人都是从应用程序开发人员开始的,我们使用现有的api和编程库。

同样的,接口是一种契约,还没有人提到接口是一种使代码的某些部分稳定的好方法。当它是一个团队项目时(或者当您正在开发其他开发人员使用的代码时),这尤其有用。所以,这里有一个具体的场景:

当您在团队中开发代码时,其他人可能会使用您编写的代码。当他们为你的(稳定的)接口编写代码时,他们会非常高兴,而当你可以自由地改变你的实现(隐藏在接口后面)而不破坏你团队的代码时,你也会非常高兴。它是信息隐藏的一种变体(接口是公开的,实现对客户端程序员隐藏)。阅读更多关于受保护的变种。

另请参阅有关接口编码的相关问题。