我曾多次看到有人提到这一点,但我不清楚这是什么意思。你什么时候,为什么要这么做?

我知道接口是做什么的,但我不清楚这一点的事实使我认为我错过了正确使用它们。

如果你要这样做

IInterface classRef = new ObjectWhatever()

你可以使用任何实现IInterface的类吗?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定什么对象将被传递,除了它实现IInterface。我不知道你需要多久做一次。

另外,如何编写一个方法来接受实现接口的对象呢?这可能吗?


当前回答

我是这个问题的后来者,但我想在这里提到的是,在GoF (Gang of Four)设计模式一书中,“为接口编程,而不是为实现编程”这一行有一些很好的讨论。

它在第18页说:

针对接口编程,而不是针对实现编程 不要将变量声明为特定具体类的实例。相反,只提交到由抽象类定义的接口。你会发现这是本书设计模式的一个共同主题。

在此之上,它是这样开始的:

仅根据抽象类定义的接口来操作对象有两个好处: 客户端仍然不知道他们使用的对象的具体类型,只要对象遵循客户端期望的接口。 客户端仍然不知道实现这些对象的类。客户端只知道定义接口的抽象类。

So in other words, don't write it your classes so that it has a quack() method for ducks, and then a bark() method for dogs, because they are too specific for a particular implementation of a class (or subclass). Instead, write the method using names that are general enough to be used in the base class, such as giveSound() or move(), so that they can be used for ducks, dogs, or even cars, and then the client of your classes can just say .giveSound() rather than thinking about whether to use quack() or bark() or even determine the type before issuing the correct message to be sent to the object.

其他回答

这里有一些关于这个问题的很好的答案,涉及到各种关于接口和松耦合代码、控制反转等等的细节。有一些相当令人兴奋的讨论,所以我想借此机会把事情分解一下,以理解为什么界面是有用的。

When I first started getting exposed to interfaces, I too was confused about their relevance. I didn't understand why you needed them. If we're using a language like Java or C#, we already have inheritance and I viewed interfaces as a weaker form of inheritance and thought, "why bother?" In a sense I was right, you can think of interfaces as sort of a weak form of inheritance, but beyond that I finally understood their use as a language construct by thinking of them as a means of classifying common traits or behaviors that were exhibited by potentially many non-related classes of objects.

例如,假设你有一款SIM游戏,并拥有以下类:

class HouseFly inherits Insect {
    void FlyAroundYourHead(){}
    void LandOnThings(){}
}

class Telemarketer inherits Person {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}
}

显然,这两个对象在直接继承方面没有任何共同之处。但是,你可以说它们都很烦人。

让我们假设我们的游戏需要有一些随机的东西让玩家在吃饭时感到厌烦。这可以是HouseFly或Telemarketer,或两者兼而有之,但你如何在一个功能上兼顾两者呢?你如何要求每个不同类型的对象以同样的方式“做他们讨厌的事情”?

要认识到的关键是,Telemarketer和HouseFly都共享一个共同的松散解释的行为,尽管它们在建模方面完全不同。所以,让我们创建一个两者都可以实现的接口:

interface IPest {
    void BeAnnoying();
}

class HouseFly inherits Insect implements IPest {
    void FlyAroundYourHead(){}
    void LandOnThings(){}

    void BeAnnoying() {
        FlyAroundYourHead();
        LandOnThings();
    }
}

class Telemarketer inherits Person implements IPest {
    void CallDuringDinner(){}
    void ContinueTalkingWhenYouSayNo(){}

    void BeAnnoying() {
        CallDuringDinner();
        ContinueTalkingWhenYouSayNo();
    }
}

我们现在有两个类,每个类都以自己的方式令人讨厌。它们不需要派生于相同的基类,也不需要共享共同的固有特征——它们只需要满足IPest的契约——这个契约很简单。你只需要变得烦人。在这方面,我们可以建立以下模型:

class DiningRoom {

    DiningRoom(Person[] diningPeople, IPest[] pests) { ... }

    void ServeDinner() {
        when diningPeople are eating,

        foreach pest in pests
        pest.BeAnnoying();
    }
}

这里我们有一个餐厅,可以接待许多食客和一些害虫——注意界面的使用。这意味着在我们的小世界中,害虫数组的成员实际上可以是Telemarketer对象或HouseFly对象。

The ServeDinner method is called when dinner is served and our people in the dining room are supposed to eat. In our little game, that's when our pests do their work -- each pest is instructed to be annoying by way of the IPest interface. In this way, we can easily have both Telemarketers and HouseFlys be annoying in each of their own ways -- we care only that we have something in the DiningRoom object that is a pest, we don't really care what it is and they could have nothing in common with other.

这个非常人为的伪代码示例(比我预期的要长得多)只是为了说明一些事情,这些事情最终为我打开了一盏灯,让我知道什么时候可以使用接口。我为这个例子的愚蠢提前道歉,但希望它有助于您的理解。而且,可以肯定的是,您在这里收到的其他回答确实涵盖了当今在设计模式和开发方法中使用接口的全部范围。

它也适用于单元测试,你可以将你自己的类(满足接口的要求)注入到依赖它的类中

为了补充现有的帖子,当开发人员同时在不同的组件上工作时,有时对接口进行编码有助于大型项目。您所需要做的就是预先定义接口并为它们编写代码,而其他开发人员则为您正在实现的接口编写代码。

有很多解释,但让它更简单。以List为例。可以使用as实现一个列表:

内部数组 链表 其他实现

通过构建一个接口,比如一个List。您只编写了List的定义或List在实际中的含义。

你可以在内部使用任何类型的实现比如数组实现。但是假设您希望由于某种原因(比如bug或性能)更改实现。然后你只需要改变声明List<String> ls = new ArrayList<String>()为List<String> ls = new LinkedList<String>()。

在代码的其他地方,你不需要改变任何东西;因为其他所有东西都建立在List的定义之上。

我坚信困难的问题应该用现实世界中简单的答案来解释。在软件设计领域,这是非常重要的。

看看你家里、学校、教堂的任何一扇门……任何建筑。

想象一下,有些门的底部有危险(所以你必须鞠躬才能与开门或关门的门互动),

或者其他人只能坐在左上角(所以,一些侏儒、残疾人或凯文·哈特不会觉得这种门很有趣,也不会觉得它很好用)。

所以设计是关键,创建程序让其他人可以开发/使用它。

接口的作用是让大型项目中的初级/高级开发人员更容易工作,这样每个人都知道自己在做什么,而不需要别人的帮助,这样你就可以尽可能顺利地工作(理论上)。


[1]:如何?通过揭示价值的形态。所以,你不需要文档,因为代码本身是不言自明的(棒极了)。


这个答案并不是针对特定语言的,而是概念驱动的(毕竟,人类是通过编写代码来创建工具的)。