2

这在某种程度上是这个问题的后续问题

假设我有一个继承树如下:

Car -> Ford -> Mustang -> MustangGT

为这些类中的一个定义接口是否有好处?例子:

ICar -> IFord -> IMustang -> IMustangGT

我可以看到也许其他类(如Chevy)想要实现IcarorIFord甚至IMustang可能,但可能不是IMustangGT因为它是如此具体。在这种情况下,接口是多余的吗?

另外,我认为任何想要实现的类IFord都肯定希望通过继承来使用它的一个继承,Ford以免重复代码。如果这是给定的,那么实施的好处是什么IFord

4

14 回答 14

16

根据我的经验,当您有多个类,每个类都需要响应相同的方法或方法时,最好使用接口,以便它们可以被其他代码互换使用,这些代码将针对这些类的公共接口编写。接口的最佳用途是当协议很重要但每个类的底层逻辑可能不同时。如果您要复制逻辑,请考虑使用抽象类或标准类继承。

针对您问题的第一部分,我建议您不要为每个类创建接口。这会不必要地使您的班级结构混乱。如果你发现你需要一个接口,你可以随时添加它。希望这可以帮助!

亚当

于 2008-11-06T13:40:55.043 回答
6

我也同意adamalex 的响应,即接口应该由应该响应某些方法的类共享。

如果类具有相似的功能,但在祖先关系中彼此不直接相关,那么接口将是将该功能添加到类而不在两者之间复制功能的好方法。(或者有多个实现,只有细微的差别。)

当我们使用汽车类比时,一个具体的例子。假设我们有以下类:

Car -> Ford   -> Escape  -> EscapeHybrid
Car -> Toyota -> Corolla -> CorollaHybrid

汽车有wheels和可以Drive()Steer()。所以这些方法应该存在于Car类中。(可能Car该类将是一个抽象类。)

Ford往下走,我们得到了和之间的区别Toyota(可能实现为汽车上标志类型的差异,同样可能是一个抽象类。)

然后,最后我们有一个EscapeCorolla类,它们是完全实现为汽车的类。

现在,我们如何制造混合动力汽车?

我们可以有一个Escapethat is的子类,EscapeHybrid它添加一个FordsHybridDrive()方法,而它的一个子类CorollaCorollaHybridwithToyotasHybridDrive()方法。这些方法基本上都在做同样的事情,但是我们有不同的方法。呸。似乎我们可以做得更好。

假设一个混合有一个HybridDrive()方法。由于我们不想最终拥有两种不同类型的混合体(在一个完美的世界中),所以我们可以制作一个IHybrid具有HybridDrive()方法的接口。

所以,如果我们想创建一个EscapeHybridCorollaHybrid,我们所要做的就是实现IHybrid接口

对于一个真实世界的例子,让我们看一下 Java。可以将一个对象与另一个对象进行比较的类实现了该Comparable接口。顾名思义,接口应该用于可比较的类,因此名称为“Comparable”。

有趣的是, Java 教程的接口课程中使用了一个汽车示例

于 2008-11-06T14:24:53.237 回答
6

您根本不应该实现任何这些接口。

类继承描述了一个对象什么(例如:它的身份)。这很好,但是大多数时候,对象是什么,远没有对象什么重要。这就是接口的用武之地。

一个接口应该描述一个对象做什么或者它的行为。我的意思是它的行为,以及给定该行为有意义的一组操作。

因此,好的接口名称通常应采用 ,IDriveable等形式IHasWheels。有时描述这种行为的最好方法是引用一个众所周知的其他对象,所以你可以说“行为就像其中之一”(例如IList:)但恕我直言,这种命名形式是少数。

鉴于这种逻辑,接口继承有意义的场景与对象继承有意义的场景完全不同——通常这些场景根本不相关。

希望这可以帮助您思考您实际需要的接口:-)

于 2009-01-22T20:58:52.697 回答
2

我想说只为您需要参考的东西制作一个界面。您可能有一些其他类或功能需要了解汽车,但多久会有一些关于福特的信息需要了解?

于 2008-11-06T13:40:56.803 回答
2

不要建造你不需要的东西。如果事实证明您需要这些接口,那么返回并构建它们是一个小的努力。

此外,在迂腐方面,我希望您实际上并没有构建看起来像这种层次结构的东西。这不是继承的用途。

于 2008-11-06T13:41:59.393 回答
2

仅在需要该级别的功能时才创建它。

重构代码始终是一个持续的过程。

有一些可用的工具可以让您在必要时提取到接口。例如http://geekswithblogs.net/JaySmith/archive/2008/02/27/refactor-visual-studio-extract-interface.aspx

于 2008-11-06T13:48:39.253 回答
1

将 ICar 和所有其余部分(Make=Ford、Model=Mustang 等)作为实现接口的类的成员。

如果您不想走检查路线,您可能想要拥有 Ford 类和例如 GM 类,并且两者都实现 ICar 以便使用多态性Make == Whatever,这取决于您的风格。

无论如何-在我看来,这些是汽车的属性,而不是相反-您只需要一个界面,因为方法很常见:制动,加速等。

福特能做其他汽车做不到的事情吗?我不这么认为。

于 2008-11-06T13:50:46.643 回答
0

我将创建前两个级别,ICar 和 IFord,然后不理会第二个级别,直到我需要第二个级别的接口。

于 2008-11-06T13:39:24.890 回答
0

仔细考虑您的对象需要如何在您的问题域中相互交互,并考虑您是否需要对特定抽象概念进行多个实现。使用接口来围绕与其他对象交互的概念提供契约。

在您的示例中,我建议福特可能是制造商,而 Mustang 是制造商福特使用的 ModelName 值,因此您可能会有更多类似的东西:

IVehichle -> CarImpl, MotorbikeImpl - has-a Manufacturer has-many ModelNames

于 2008-11-06T13:57:43.790 回答
0

在这个关于interface 和 class 之间区别的答案中,我解释说:

  • 接口暴露了一个概念什么(在编译时“什么有效的”),并用于(MyInterface x = ...)
  • 类公开概念的作用(实际上在运行时执行),并用于值或对象(MyClass x 或 aMyClass.method() )

因此,如果您需要将不同的福特子类存储到“福特”变量(“价值”的概念)中,请创建一个 IFord。否则,在你真正需要它之前不要打扰。

这是一个标准:如果不满足,福特可能就没用了。
如果满足,则适用前面答案中公开的其他标准:如果 Ford 具有比 Car 更丰富的 API,则 IFord 可用于多态性目的。如果没有,ICar 就足够了。

于 2008-11-06T14:05:02.420 回答
0

在我看来,接口是一种强制要求类实现某个签名或(如我所想的那样)某种“行为”的工具对我来说,我认为如果我的接口开头的 Capital I 名称为一个人称代词,我尝试命名我的接口,以便可以这样阅读它们...... ICanFly、IKnowHowToPersistMyself IAmDisplayable 等......所以在你的例子中,我不会创建一个接口来镜像任何特定的完整公共签名班级。我会分析公共签名(行为),然后将成员分成更小的逻辑组(越小越好),例如(使用您的示例)IMove、IUseFuel、ICarryPassengers、ISteerable、IAccelerate、IDepreciate 等......然后应用这些接口到我系统中任何其他类需要它们

于 2008-11-06T14:25:03.313 回答
0

一般来说,考虑这一点(以及 OO 中的许多问题)的最佳方式是考虑合同的概念。

合同被定义为两方(或多方)之间的协议,其中规定了每一方必须履行的具体义务;在一个程序中,这是一个类将提供的服务,以及您必须为类提供什么才能获得服务。接口声明了实现该接口的任何类都必须满足的契约。

但是,考虑到这一点,您的问题在某种程度上取决于您使用的语言以及您想要做什么。

在从事 OO 多年(比如,天哪,30 年)之后,我通常会为每个合约编写一个接口,尤其是在 Java 中,因为它使测试变得非常容易:如果我有一个类的接口,我可以构建轻松地模拟对象,几乎是微不足道的。

于 2008-11-06T14:28:32.777 回答
0

接口旨在成为通用公共 API,用户将被限制使用此公共 API。除非您打算让用户使用 的特定于类型的方法IMustangGT,否则您可能希望将接口层次结构限制为ICarIExpensiveCar

于 2008-11-06T14:33:08.867 回答
-1

仅从接口和抽象类继承。

如果您有几个几乎相同的类,并且您需要实现大多数方法,请结合购买其他对象使用和接口。
如果 Mustang 类如此不同,那么不仅要创建接口 ICar,还要创建 IMustang。
所以 Ford 和 Mustang 类可以从 ICar 继承,而 Mustang 和 MustangGT 可以从 ICarIMustang 继承。

如果你实现了 Ford 类并且方法与 Mustang 相同,请从 Mustang 购买:

class Ford{
  public function Foo(){
    ...
    Mustang mustang  = new Mustang();
    return mustang.Foo();
}
于 2008-11-06T13:49:57.863 回答