继承和接口

时间:2008-11-06 13:34:30

标签: oop inheritance interface ooad

这对此question来说是一个后续问题。

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

Car -> Ford -> Mustang -> MustangGT

这些类的每个定义接口是否有好处?例如:

ICar -> IFord -> IMustang -> IMustangGT

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

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

14 个答案:

答案 0 :(得分:16)

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

在回答问题的第一部分时,我建议不要为每个类创建一个界面。这会不必要地混乱你的类结构。如果您发现需要界面,可以随时添加。希望这有帮助!

亚当

答案 1 :(得分:6)

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

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

虽然我们正在使用汽车类比,一个具体的例子。假设我们有以下类:

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

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

走下去,我们得到了FordToyota之间的区别(可能是因为车上标志类型的差异,也可能是一个抽象类。)

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

现在,我们如何制作 Hybrid 车辆?

我们可以有一个Escape的子类,EscapeHybrid添加FordsHybridDrive()方法,Corolla的子类CorollaHybridToyotasHybridDrive() 1}}方法。这些方法基本上都是做同样的事情,但我们有不同的方法。 Yuck。好像我们可以做得更好。

假设混合使用HybridDrive()方法。由于我们不希望最终有两种不同类型的混合(在完美的世界中),因此我们可以创建一个IHybrid方法,其中HybridDrive()方法。

所以,如果我们要制作EscapeHybridCorollaHybrid,我们所要做的就是实施IHybrid接口< /强>

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

就感兴趣而言,car exampleInterfaces课程中使用了Java Tutorial

答案 2 :(得分:6)

You shouldn't implement any of those interfaces at all.

类继承描述对象 (例如:它的身份)。这很好,但是大多数情况下对象 是什么,远不如对象那么重要。这就是接口的用武之地。

界面应描述对象做什么,或者它的作用。通过这个我的意思是它的行为,以及在给定行为的情况下有意义的操作集。

因此,良好的界面名称通常应为IDriveableIHasWheels等形式。有时,描述此行为的最佳方式是引用一个众所周知的其他对象,因此您可以说“行为就像其中之一”(例如:IList),但恕我直言,这种命名形式属于少数。< / p>

鉴于逻辑,接口继承有意义的场景与对象继承有意义的场景完全不同 - 通常这些场景与彼此都是。

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

答案 3 :(得分:2)

我只说为你需要引用的东西制作一个界面。您可能还有其他一些需要了解汽车的类别或功能,但是有多少东西需要了解福特?

答案 4 :(得分:2)

不要构建你不需要的东西。如果事实证明你需要接口,那么返回构建它们是一件很小的努力。

另外,在迂腐的一面,我希望你实际上并没有构建看起来像这种层次结构的东西。这不是继承应该用于什么。

答案 5 :(得分:2)

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

重新分解代码始终处于正在进行的过程中。

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

答案 6 :(得分:1)

制作ICar和其余所有(Make = Ford,Model = Mustang和stuff)作为实现接口的类的成员。

你可能想拥有你的福特课程,例如GM课程,并且如果你不想沿着检查Make == Whatever的路线走下去,那么它们都会实现ICar以便使用多态,这取决于你的风格。

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

福特可以做其他车不能做的事吗?我不这么认为。

答案 7 :(得分:0)

我会创建前两个级别,ICar和IFord,然后单独留下第二级,直到我需要第二级的接口。

答案 8 :(得分:0)

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

在您的示例中,我建议福特可能是制造商,Mustang是制造商福特使用的ModelName值,因此您可能会更喜欢:

IVehichle - &gt; CarImpl,MotorbikeImpl - 有一个制造商有很多ModelNames

答案 9 :(得分:0)

在关于difference between interface and class的答案中,我解释说:

  • 接口公开 的概念(在编译时“有什么 ”有效),并用于 值< / em> (MyInterface x = ...)
  • 类公开了做什么(实际上是在运行时执行),用于值或对象(MyClass x或aMyClass.method())

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

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

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

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

考虑到这一点,您的问题在某种程度上取决于您使用的语言和您想要做的事情。

经过多年的OO(比如,天啊,30年),我通常会为每个合同编写一个接口,特别是在Java中,因为它使测试变得如此简单:如果我有一个类的接口,我几乎可以轻松地构建模拟对象。

答案 12 :(得分:0)

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

答案 13 :(得分:-1)

仅从Interfaces和抽象类继承。

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

如果您实施福特班并且方法与野马相同,请从野马购买:

class Ford{
  public function Foo(){
    ...
    Mustang mustang  = new Mustang();
    return mustang.Foo();
}