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

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

如果你要这样做

IInterface classRef = new ObjectWhatever()

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

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


当前回答

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

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

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

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

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

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


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


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

其他回答

你应该看看反转控制:

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

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

IInterface classRef = new ObjectWhatever();

你可以这样写:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

问:——……“你能使用任何实现接口的类吗?” A:是的。 问:——……“你什么时候需要这样做?” 答:-每次你都需要一个实现接口的类。

注意:我们不能实例化一个没有被类实现的接口- True。

为什么? 因为接口只有方法原型,没有定义(只有函数名,没有它们的逻辑)

AnIntf anInst = new class(); //我们可以这样做,只有当类实现AnIntf。 // anInst将有类引用。


注意:现在我们可以理解如果Bclass和Cclass实现相同的Dintf会发生什么。

Dintf bInst = new Bclass();  
// now we could call all Dintf functions implemented (defined) in Bclass.

Dintf cInst = new Cclass();  
// now we could call all Dintf functions implemented (defined) in Cclass.

我们拥有:相同的接口原型(接口中的函数名),调用不同的实现。

参考书目: 原型——维基百科

下面是一个简单的示例,用于说明如何对航班预订系统进行编程。

//This interface is very flexible and abstract
    addPassenger(Plane seat, Ticket ticket); 

//Boeing is implementation of Plane
    addPassenger(Boeing747 seat, EconomyTicket ticket); 
    addPassenger(Cessna, BusinessClass ticket);


    addPassenger(J15, E87687); 

如果我正在编写一个新类Swimmer来添加swim()功能,并且需要使用类的对象说Dog,并且这个Dog类实现了声明swim()的接口Animal。

在层次的顶端(动物),它是非常抽象的,而在底层(狗),它是非常具体的。我认为“面向接口编程”的方式是,当我编写Swimmer类时,我想针对层次结构最高的接口编写代码,在本例中是Animal对象。接口没有实现细节,因此使代码松耦合。

实现细节可以随着时间的推移而改变,但是,它不会影响剩余的代码,因为您所与之交互的是接口而不是实现。你不关心实现是什么样的……您所知道的是,将会有一个实现该接口的类。