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

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

如果你要这样做

IInterface classRef = new ObjectWhatever()

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

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


当前回答

为一个接口编程就是在说:“我需要这个功能,我不在乎它来自哪里。”

Consider (in Java), the List interface versus the ArrayList and LinkedList concrete classes. If all I care about is that I have a data structure containing multiple data items that I should access via iteration, I'd pick a List (and that's 99% of the time). If I know that I need constant-time insert/delete from either end of the list, I might pick the LinkedList concrete implementation (or more likely, use the Queue interface). If I know I need random access by index, I'd pick the ArrayList concrete class.

其他回答

因此,为了做到这一点,接口的优点是我可以将方法的调用与任何特定的类分开。而是创建接口的实例,其中的实现来自我选择的实现该接口的任何类。因此,允许我拥有许多类,它们具有相似但略有不同的功能,并且在某些情况下(与接口的意图相关的情况)并不关心它是哪个对象。

例如,我可以有一个移动界面。一个方法可以让一些东西“移动”,任何实现移动接口的对象(Person, Car, Cat)都可以被传递进来并被告知移动。如果每个方法都不知道类的类型,那么它就是类。

让我们先从一些定义开始:

一个对象的操作所定义的所有签名的集合称为该对象的接口

输入n.指定接口

上面定义的接口的一个简单例子是所有PDO对象方法,如query()、commit()、close()等,作为一个整体,而不是分开。这些方法,即它的接口定义了可以发送到对象的完整消息和请求集。

上面定义的类型是一个特定的接口。我将使用创建的形状界面来演示:draw(), getArea(), getPerimeter()等。

如果一个对象是Database类型的,我们的意思是它接受数据库接口、query()、commit()等的消息/请求。对象可以有多种类型。只要实现了接口,就可以让数据库对象具有形状类型,在这种情况下,这将是子类型。

许多对象可以是许多不同的接口/类型,并以不同的方式实现该接口。这允许我们替换对象,让我们选择使用哪个对象。也称为多态性。

客户机将只知道接口,而不知道实现。

因此,从本质上讲,编程到一个接口将涉及到一些类型的抽象类,如只指定接口的Shape,即draw(), getCoordinates(), getArea()等。然后让不同的具体类实现这些接口,比如圆形类,方形类,三角形类。因此,针对接口而不是实现编程。

我曾经给学生的一个具体例子是他们应该写作

List myList = new ArrayList(); // programming to the List interface

而不是

ArrayList myList = new ArrayList(); // this is bad

在一个短程序中,它们看起来完全相同,但如果在程序中继续使用myList 100次,就会开始看到区别。第一个声明确保只调用myList上由List接口定义的方法(因此没有ArrayList特定的方法)。如果您以这种方式对接口进行了编程,那么稍后您就可以确定您确实需要

List myList = new TreeList();

你只需要在这一点上修改代码。您已经知道,其余的代码不会因为更改实现而被破坏,因为您对接口进行了编程。

当您谈论方法参数和返回值时,好处甚至更明显(我认为)。举个例子:

public ArrayList doSomething(HashMap map);

该方法声明将您绑定到两个具体实现(ArrayList和HashMap)。一旦从其他代码调用该方法,对这些类型的任何更改都可能意味着您将不得不更改调用代码。最好是根据接口进行编程。

public List doSomething(Map map);

现在,不管您返回什么样的List,或者作为参数传入什么样的Map。在doSomething方法中所做的更改不会强制您更改调用代码。

If you program in Java, JDBC is a good example. JDBC defines a set of interfaces but says nothing about the implementation. Your applications can be written against this set of interfaces. In theory, you pick some JDBC driver and your application would just work. If you discover there's a faster or "better" or cheaper JDBC driver or for whatever reason, you can again in theory re-configure your property file, and without having to make any change in your application, your application would still work.

你应该看看反转控制:

Martin Fowler:控制反转容器和依赖注入模式 维基百科:控制反转

在这种情况下,你不会这样写:

IInterface classRef = new ObjectWhatever();

你可以这样写:

IInterface classRef = container.Resolve<IInterface>();

这将进入容器对象中基于规则的设置,并为您构造实际对象,该对象可以是ObjectWhatever。重要的是,您可以将此规则替换为完全使用另一种类型对象的规则,而您的代码仍然可以工作。

如果我们不考虑IoC,那么您可以编写的代码知道它可以与执行特定操作的对象进行对话,但不知道是哪种类型的对象或它如何执行操作。

这将在传递参数时派上用场。

至于你带圆括号的问题“另外,你如何编写一个方法来接受一个实现接口的对象?”这可能吗?”,在c#中,你可以简单地使用接口类型作为参数类型,如下所示:

public void DoSomethingToAnObject(IInterface whatever) { ... }

这直接插入到“与做特定事情的对象对话”中。上面定义的方法知道从对象中期望得到什么,它实现了IInterface中的所有内容,但它并不关心它是哪种类型的对象,只关心它遵守契约,这就是接口。

例如,你可能对计算器很熟悉,也可能在你的生活中使用过不少计算器,但大多数时候它们都是不同的。另一方面,你知道标准计算器应该如何工作,所以你能够使用所有的计算器,即使你不能使用每个计算器都没有的特定功能。

这就是界面的美妙之处。你可以写一段代码,它知道它会得到传递给它的对象,它可以从这些对象中期待特定的行为。它并不关心它是什么类型的对象,只关心它支持所需的行为。

让我给你一个具体的例子。

我们为windows窗体定制了翻译系统。这个系统循环遍历表单上的控件,并翻译每个控件中的文本。这个系统知道如何处理基本的控件,比如拥有文本属性的控件类型,以及类似的基本东西,但对于任何基本的东西,它都做不到。

现在,由于控件继承自我们无法控制的预定义类,我们可以做以下三件事之一:

为我们的翻译系统提供支持,以检测它正在使用哪种类型的控件,并翻译正确的位(维护的噩梦) 将支持构建到基类中(不可能,因为所有控件都继承自不同的预定义类) 添加接口支持

所以是nr. 3。我们所有的控件都实现了ILocalizable,这是一个提供给我们一个方法的接口,能够将“本身”转换为翻译文本/规则的容器。因此,表单不需要知道它找到了哪种类型的控件,只需要知道它实现了特定的接口,并且知道可以调用某个方法来本地化该控件。