2

我没有得到接口与多态性的连接。
对我来说,多态性是关于使用抽象方法或虚拟方法+覆盖以不同的方式为某些不同的具体类执行方法,因此在我看来这仅与继承有关,但是如何使用接口覆盖方法?您如何使用接口以不同的方式执行相同的方法并让对象根据其具体类型决定要做什么?

谢谢

4

3 回答 3

3

正如 Andreas Hartl 在他关于继承与继承的文章中所述。接口

许多高级语言支持继承和接口,对于刚接触这些概念的人来说,有时并不清楚该选择哪一种。尽管语言在对继承和接口的确切处理上有所不同,但基本原理通常是相同的,因此本技巧应该适用于大多数语言。

继承意味着我们从另一个类(基类)派生一个类(派生类)。派生类是基类的扩展。它包含基类的所有特性(方法和数据成员),可以用新特性扩展它,并且可以重新实现基类的虚方法。一些语言,如 C++,支持多重继承,派生类可以有多个基类,但通常继承仅限于单个基类。

接口通常只能定义方法而不能定义数据成员(但例如 C# 允许接口内的属性形式的数据成员),并且一个类总是可以实现多个接口。接口只包含没有实现的方法定义,实现接口的类提供实现。

因此,使用继承,你编写了一个带有方法实现的基类,当你从它派生一个类时,这个类将继承基类的所有内容,并立即能够使用它的特性。另一方面,接口只是方法签名的契约,想要实现接口的类被迫提供接口所有方法的实现。

那你什么时候用哪个?在某些情况下,语言已经规定了你使用什么:如果你需要你的类有多个“父母”,你不能在不支持多重继承的语言中使用继承。如果要重用库对象,则必须使用拟合概念,具体取决于该库对象是类还是接口。

但是,如果您可以自由选择,该使用哪个?基本上,基类描述并实现了相关类型的共同行为,而接口描述了不相关类型可以实现的功能。继承描述“是一种”关系,接口描述“行为类似”关系。例如,假设您正在编写一个飞行模拟器。例如,您将存储在列表中的基本实体将是“飞机”。您的具体类型将是“协和”和“幻影”。那么应该如何对这三种类型进行建模呢?Concorde 和 Phantom 是相关的,它们都是飞机并且共享数据,如“重量”或“最大速度”和功能,如“加速”,因此我们可以通过继承对它们进行建模。'飞机' 将是具有通用数据和方法的基类,“Concorde”和“Phantom”将派生自“Airplane”。我们可以说两者都是专业飞机,这就是为什么人们常说继承意味着专业化。现在假设我们还在我们的程序中添加了一个类“Pilot”,并且我们希望让用户能够保存游戏并在以后加载它。所以当他保存游戏时,我们需要保存所有飞机的状态和所有飞行员的状态。我们想在一个函数中做到这一点,该函数只接受所有可保存对象的列表。那么我们如何建模呢?要回答这个问题,我们必须看看我们想要保存的不同类型。飞行员和飞机。很明显,它们根本没有关系。它们不共享通用数据和通用功能。我们可以看到,编写一个基类 'Saveable' 并从中派生 Pilot 和 Airplane 没有什么意义,因为 Saveable 中的任何代码都不能被 Airplane 或 Pilot 重用,因为它们没有共同的属性。在这种情况下,接口是最好的解决方案。我们可以使用 Save() 方法编写一个接口“ISaveable”。Pilot 然后可以通过保存他的名字来实现 ISaveable.Save(),而 Airplane 可以保存它当前的速度和坐标。

如您所见,类之间关系的清晰图像通常可以明确选择:对相关类型使用继承,其中每个派生类“都是”基类。对具有一些通用功能的不相关类型使用接口。

以下是继承和接口需要考虑的更多要点:

  • 接口是固定的。当您更改接口时,您必须更改实现该接口的每个类。但是当您更改基类时,每个派生类都将获得新功能,这两者都很好(如果您在某些基类方法实现中进行错误修复,则使用该方法的派生类将获得错误修复,而无需您进行更改它)或坏的(如果基类的更改引入了新的错误,所有使用该方法的派生类也将被错误)。

  • 接口通常更灵活,因为在大多数语言中,您只能从一个类派生,但要实现许多接口

  • 接口有助于保护内部类:假设类 A 有一个类 B 的内部对象 b。当 A 中的方法返回一个指向 b 的指针或引用时,调用此方法的代码现在可以访问整个对象 b,这可以如果 A 只想暴露 b 的某些成员,则很危险。如果您只使用可以安全公开的成员创建接口 I,则可以解决此问题。当 B 实现此接口,并且您在 A 中的方法通过 I 指针或引用返回 b 时,外部代码只能通过接口执行您允许的操作。

于 2012-08-06T22:22:05.570 回答
3

多态作为一个概念不需要继承,尽管在许多语言中继承是实现它的唯一方法。一些语言,如 smalltalk,允许您以多态方式使用实现相同成员和属性集的任何类型。如果它长得像鸭子,叫起来像鸭子,走路像鸭子,你可以把它当作鸭子。

多态性只是通过提供与原始对象相同的方式访问和使用它来将一个对象视为另一个对象的能力。里氏替换原则最好地说明了这一点。这称为“接口”或有时称为“合同”,因为它定义了另一个对象可以用来对对象做有趣事情的“签名”。

在 C# 中,您可以从接口或其他(非密封)类继承。不同之处在于接口不提供任何实际的存储或方法(仅提供它们的“签名”),它只是一个定义。不能实例化接口,只能实例化实现接口的对象。

类实现接口(例如 IDisposable)的方式与基于蓝图建造房屋的方式相同。如果你用相同的蓝图建造两栋房子,那么每栋房子都有完全相同的“界面”,它们可能有不同颜色的油漆或地毯,但它们的功能完全相同,但它们是两座截然不同的房子,有很多各种事物如何运作的差异。

谈到 C#,只需知道接口说明了实现它的对象必须具有哪些属性或成员。同样,在 C# 中,一个很大的区别是您可以继承多个接口,但只能继承一个类。(即public class Test : BaseClass, IDisposable, ITest, IFooBar

于 2012-08-06T22:37:47.553 回答
0

考虑这个...

public int SomeMethod(SomeBaseClass object)
{
  // Pass in a descendant classe that implements / overrides some method in SomebaseClass
}

public int SomeMethod(ISomeInterface intf)
{
   // pass in concrete classes that implement some ISomeInterface function 
}

这是多态行为的基本本质,一个通用的契约,专门由一个专家类来实现。

于 2012-08-06T22:24:54.850 回答