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

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

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

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


当前回答

假设你想要模拟当你试图睡觉时可能发生的烦恼。

接口前的模型

class Mosquito {
    void flyAroundYourHead(){}
}

class Neighbour{
    void startScreaming(){}
}

class LampJustOutsideYourWindow(){
    void shineJustThroughYourWindow() {}
}

正如你清楚地看到的,当你试图睡觉时,许多“事情”都可能令人讨厌。

使用没有接口的类

但是在使用这些类时,我们遇到了一个问题。他们毫无共同之处。您必须分别调用每个方法。

class TestAnnoyingThings{
    void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
         if(mosquito != null){
             mosquito.flyAroundYourHead();
         }
         if(neighbour!= null){
             neighbour.startScreaming();
         }
         if(lamp!= null){
             lamp.shineJustThroughYourWindow();
         }
    }
}

带有接口的模型

为了克服这个问题,我们可以引入一个iterface

interface Annoying{
   public void annoy();

}

并在类中实现它

class Mosquito implements Annoying {
    void flyAroundYourHead(){}

    void annoy(){
        flyAroundYourHead();
    }
}

class Neighbour implements Annoying{
    void startScreaming(){}

    void annoy(){
        startScreaming();
    }
}

class LampJustOutsideYourWindow implements Annoying{
    void shineJustThroughYourWindow() {}

    void annoy(){
        shineJustThroughYourWindow();
    }
}

接口使用

这将使这些类的使用更容易

class TestAnnoyingThings{
    void testAnnoyingThinks(Annoying annoying){
        annoying.annoy();
    }
}

其他回答

As several people have probably already answered, interfaces can be used to enforce certain behaviors between classes that will not implement those behaviors the same way. So by implementing an interface you are saying that your class has the behavior of the interface. The IAnimal interface would not be a typical interface because Dog, Cat, Bird, etc. classes are types of animals, and should probably extend it, which is a case of inheritance. Instead, an interface would be more like animal behavior in this case, such as IRunnable, IFlyable, ITrainable, etc.

接口有很多好处,其中一个关键就是可插拔性。例如,声明一个具有List参数的方法将允许传入实现List接口的任何东西,允许开发人员在以后删除和插入不同的列表,而不必重写大量代码。

您可能永远不会使用接口,但如果您正在从头开始设计一个项目,特别是某种类型的框架,您可能会想要熟悉它们。

我建议大家阅读Coad、Mayfield和Kern撰写的《Java设计》中关于接口的章节。它们比一般的介绍性文本解释得好一点。如果你不使用Java,你可以只阅读本章的开头,主要是一些概念。

扩展一下Larsenal所说的。接口是所有实现类都必须遵循的契约。因此,您可以使用一种称为契约编程的技术。这允许您的软件变得独立于实现。

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.

我偶尔也会使用接口,下面是我最新的用法(名称已经概括了):

我在WinForm上有一堆需要将数据保存到业务对象的自定义控件。一种方法是分别调用每个控件:

myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);

这个实现的问题是,每当我添加一个控件,我必须进入我的“保存数据”方法,并添加新的控件。

我改变了我的控件来实现一个ISaveable接口,它有一个方法SaveToBusinessObject(…),所以现在我的“保存数据”方法只是通过控件迭代,如果它发现一个是ISaveable,它调用SaveToBusinessObject。所以现在当需要一个新的控件时,所有人要做的就是在该对象中实现ISaveable(并且永远不要触及其他类)。

foreach(Control c in Controls)
{
  ISaveable s = c as ISaveable;

  if( s != null )
      s.SaveToBusinessObject(myBusinessObject);
}

接口通常未被意识到的好处是本地化修改。定义之后,您很少会更改应用程序的整体流程,但通常会在细节级别上进行更改。当您将细节保存在特定对象中时,ProcessA中的更改将不会影响ProcessB中的更改。(基类也有这个好处。)

编辑:另一个好处是行动的专一性。就像在我的例子中,我所要做的就是保存数据;我不关心它是什么类型的控件,或者它是否可以做任何其他事情——我只想知道我是否可以保存控件中的数据。它使我的保存代码非常清晰——没有检查它是否为文本、数字、布尔值或任何东西,因为自定义控件处理所有这些。

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

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

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

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