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

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

如果你要这样做

IInterface classRef = new ObjectWhatever()

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

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


当前回答

问:——……“你能使用任何实现接口的类吗?” 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.

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

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

其他回答

面向接口编程非常棒,它促进了松耦合。正如@lassevk提到的,控制反转是一个很好的应用。

此外,研究SOLID原则。这是一个系列视频

它通过硬编码(强耦合示例),然后查看接口,最后进展到IoC/DI工具(NInject)。

接口编程与我们在Java或。net中看到的抽象接口完全没有关系。它甚至不是面向对象的概念。

它的意思是不要乱动对象或数据结构的内部结构。使用抽象程序接口(API)与数据交互。在Java或c#中,这意味着使用公共属性和方法,而不是原始字段访问。对于C语言来说,这意味着使用函数而不是原始指针。

EDIT:对于数据库,这意味着使用视图和存储过程,而不是直接访问表。

你应该看看反转控制:

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

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

IInterface classRef = new ObjectWhatever();

你可以这样写:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

接口类似于契约,您希望实现类实现在契约(接口)中编写的方法。由于Java不提供多重继承,“编程到接口”是实现多重继承的好方法。

如果您有一个类a,它已经扩展了其他一些类B,但是您希望这个类a也遵循某些指导方针或实现某个契约,那么您可以通过“编程到接口”策略来实现。

c++的解释。

把接口看作是类的公共方法。

然后你可以创建一个“依赖”于这些公共方法的模板来执行它自己的函数(它在类的公共接口中进行函数调用)。假设这个模板是一个容器,就像Vector类一样,它所依赖的接口是一个搜索算法。

任何定义函数/接口Vector调用的算法类都将满足“契约”(就像有人在原始回复中解释的那样)。算法甚至不需要是相同的基类;唯一的要求是向量所依赖的函数/方法(接口)在你的算法中定义。

所有这些的重点是,您可以提供任何不同的搜索算法/类,只要它提供了Vector所依赖的接口(冒泡搜索、顺序搜索、快速搜索)。

您可能还想设计其他容器(列表、队列),通过让它们实现搜索算法所依赖的接口/契约,来利用与Vector相同的搜索算法。

这节省了时间(OOP原则“代码重用”),因为你可以一次编写一个算法,而不是一遍又一遍地针对你创建的每个新对象,而不会因为过度生长的继承树而使问题过于复杂。

至于“错过”事情是如何运作的;这是非常重要的(至少在c++中),因为这是大多数标准模板库框架的运作方式。

当然,当使用继承和抽象类时,接口编程的方法会发生变化;但原理是一样的,你的公共函数/方法是你的类接口。

这是一个巨大的主题,也是设计模式的基石原则之一。