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

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

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

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


当前回答

一个代码示例(结合了Andrew的代码和我的额外的关于“接口的目的是什么”的代码),也说明了为什么在不支持多重继承的语言(c#和java)上接口而不是抽象类:

interface ILogger
{
    void Log();
}
class FileLogger : ILogger
{
    public void Log() { }
}
class DataBaseLogger : ILogger
{
    public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
    public void Log() { }
}

注意,FileLogger和DataBaseLogger不需要接口(可以是Logger的抽象基类)。但是考虑到您需要使用第三方记录器,它迫使您使用基类(假设它公开了您需要使用的受保护的方法)。由于语言不支持多重继承,您将无法使用抽象基类方法。

底线是:尽可能使用接口来获得代码的额外灵活性。您的实现不那么受约束,因此它能更好地适应变化。

其他回答

I like Jimmy's answer a lot, but I feel I need to add something to it. The key to the whole thing is the "able" in IProcessable . It indicates a capability (or property, but meaning "intrinsic quality", not in the sense of C# properties) of the object that implements the interface. IAnimal is probably not a good example for an interface, but IWalkable might be a good interface to have if your system has many things that can walk. You might have classes derived from Animal such as Dog, Cow, Fish, Snake. The first two would probably implement IWalkable, the latter two don't walk, so they wouldn't. Now you ask "why not just have another superclass, WalkingAnimal, that Dog and Cow derive from?". The answer is when you have something completely outside the inheritance tree that also can walk, such as a robot. Robot would implement IWalkable, but probably wouldn't derive from Animal. If you want a list of things that can walk, you type it as IWalkable and you can put all walking animals plus robots in the list.

现在,将IWalkable替换为IPersistable这样更像软件的东西,这样的类比就更接近你在实际程序中看到的情况了。

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

接口前的模型

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();
    }
}

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

我在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中的更改。(基类也有这个好处。)

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

别太担心。很多开发人员很少需要编写接口。您将经常使用. net框架中可用的接口,但如果您觉得没有必要在短时间内编写一个接口,这也没什么好奇怪的。

我经常给别人的例子是,如果你有一个帆船职业和一个毒蛇职业。它们分别继承了Boat类和Car类。现在假设您需要遍历所有这些对象并调用它们的Drive()方法。你也可以编写如下代码:

if(myObject is Boat)
    ((Boat)myObject).Drive()
else
    if (myObject is Car)
        ((Car)myObject).Drive()

这样写会简单得多:

((IDrivable)myObject).Drive()

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,你可以只阅读本章的开头,主要是一些概念。