【问题标题】:Inheritance and interfaces继承和接口
【发布时间】:2010-09-21 02:06:08
【问题描述】:

这是question 的后续问题。

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

Car -> Ford -> Mustang -> MustangGT

为这些类中的每个定义接口有什么好处?示例:

ICar -> IFord -> IMustang -> IMustangGT

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

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

【问题讨论】:

    标签: oop inheritance interface ooad


    【解决方案1】:

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

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

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

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

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

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

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

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

        【讨论】:

          【解决方案4】:

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

          【讨论】:

            【解决方案5】:

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

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

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

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

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

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

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

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

            我们可以有一个Escape 的子类,即EscapeHybrid,它添加了一个FordsHybridDrive() 方法,以及一个Corolla 的子类,即CorollaHybridToyotasHybridDrive() 方法。这些方法基本上都在做同样的事情,但是我们有不同的方法。 糟糕。看来我们可以做得更好。

            假设一个混合有一个HybridDrive() 方法。由于我们不想最终拥有两种不同类型的混合体(在理想情况下),因此我们可以创建一个具有 HybridDrive() 方法的 IHybrid 接口。

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

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

            出于兴趣,car exampleJava TutorialInterfaces 课程中使用。

            【讨论】:

              【解决方案6】:

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

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

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

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

              【讨论】:

                【解决方案7】:

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

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

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

                【讨论】:

                  【解决方案8】:

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

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

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

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

                  【讨论】:

                    【解决方案9】:

                    仅从接口和抽象类继承。

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

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

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

                    【讨论】:

                      【解决方案10】:

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

                      重构代码始终在进行中。

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

                      【讨论】:

                        【解决方案11】:

                        不要构建你不需要的东西。如果事实证明您需要这些接口,只需稍作努力就可以返回并构建它们。

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

                        【讨论】:

                          【解决方案12】:

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

                          【讨论】:

                            【解决方案13】:

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

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

                            亚当

                            【讨论】:

                            • "如果你发现你需要一个接口,你可以随时添加它。" YAGNI 原则就在这里。将所有此类决定推迟到需要的时候。
                            【解决方案14】:

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

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2011-07-31
                              • 2011-10-05
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2011-06-15
                              相关资源
                              最近更新 更多