【问题标题】:Recommendations for Abstract Classes vs Interfaces抽象类与接口的建议
【发布时间】:2013-11-25 12:27:21
【问题描述】:

我知道接口和抽象类的区别。现在我想知道我到底需要在哪里使用接口而不是抽象类,反之亦然。

我提到的文章Recommendations for Abstract Classes vs. Interfaces

在那个

这里有一些建议可以帮助您决定是使用接口还是抽象类来为组件提供多态性。

  1. 如果您预计创建组件的多个版本,请创建 一个抽象类。抽象类提供了一种简单易行的方法 版本你的组件。通过更新基类,所有继承 类会随着变化而自动更新。接口,在 另一方面,一旦创建就无法更改。如果一个新版本的 接口是必需的,你必须创建一个全新的接口。

  2. 如果您正在创建的功能对各种不同的对象都很有用,请使用接口。抽象类应该 主要用于密切相关的对象,而 接口最适合提供通用功能 不相关的类。

  3. 如果您正在设计小而简洁的功能,请使用 接口。如果您正在设计大型功能单元,请使用 抽象类。

  4. 如果您想在组件的所有实现中提供通用的已实现功能,请使用抽象类。抽象类允许您部分实现您的类,而接口不包含任何成员的实现。

我不确定第 3 点。我需要将组件的所有小功能都放入接口中吗?

任何现实世界/可理解的例子可以帮助我做出选择吗?

【问题讨论】:

    标签: c# java oop interface


    【解决方案1】:

    接口就像合同。如果您的代码的任何部分应该依赖于某些方法或属性的存在,那么接口是一个好主意。它还为单元测试提供了更多空间。

    很多时候我都定义了一个接口和实现它的抽象类。这样,您可以在不从基类派生的情况下实现接口。

    对于真实的单词示例,例如,考虑一个消息网关。 请注意,以下实现并不是 OOP 完美的。我只是不想创建这么多的类和接口。

    interface IMessageSender
    {
        string From { get; set; }
        string To { get; set; }
        string Message { get; set; }
    
        void Send();
    }
    
    abstract class MessageSenderWithSubjectBase : IMessageSender
    {
        string From { get; set; }
        string To { get; set; }
        string Message { get; set; }
    
        string Subject { get; set; }
    
        abstract void Send();
    }
    
    class EmailSender : MessageSenderWithSubjectBase
    {
        override void Send() { // send email }
    }
    
    class SmsSender : IMessageSender
    {
        override void Send() { // send sms }
    }
    

    看,短信没有主题。您也可以从抽象类派生而忽略主题,但这不是一个清晰的设计。更不用说基类中有通用方法的情况,你知道你根本不需要。相反,您可以为没有主题的消息创建一个基类,或者只实现接口。

    当您需要在代码中发送消息时,您可能会从某种工厂获得消息发送者,并且您可以相信它能够发送您的消息,因为它实现了接口。你可以像那样抽象。

    虽然这个答案没有直接回答你的问题,但那是因为我不认为你可以像你读过的那样创建规则。给定时间和多行代码,您最终会明白何时需要接口、抽象类或同时需要两者。

    【讨论】:

      【解决方案2】:

      想象你正在创建一个动画并且你有车辆和人都应该从 IMovable 继承,因此在这里独立于类起源你应该能够初始化一个 IMovable 数组并调用在你的接口中定义的移动方法,这将让你当您调用 move 时,可以通过一次调用方法让人走路和汽车的车轮转动,因为这里每个类都有自己的实现 Move 的行为 对于第二部分,请查看此链接http://dofactory.com/Patterns/PatternAbstract.aspx#_self2,因为它完美地解释了为什么您应该使用抽象而不是接口,例如 carnivore 有一个通用方法 Eat,默认情况下可以从所有子类中使用,因为 carnivore 应该吃草食动物。
      希望对您有所帮助

      【讨论】:

        【解决方案3】:

        我通常只是查看代码并尝试找出是否存在可以“按原样”用于相关层次结构中不同对象的实际常用方法。 IE。如果类 A 和 B 继承相同的共同祖先 C,因为它们是相似的 - 是否有任何功能对它们来说是相同的?在这种情况下,C 可能是具有方法 doSomething() 的抽象类,只需要在 C 中指定(如果 C 是接口,则 A 和 B 都必须提供自己的 doSomething() 方法的实现)。

        我认为这就是#3 所暗示的,如果您使用接口设计大型功能单元,则所有内容都需要重新实现,这意味着大量代码。如果没有做类似事情的“通用”方法,那么实现就太不同了,根本不应该属于同一个接口。 (但是,这种推理在很大程度上假设使用的所有重要类都是某些适当继承结构的一部分,但情况可能并非总是如此,例如,如果您使用 3rd 方 API。)

        【讨论】:

          【解决方案4】:

          回答第(3)点。

          如果您正在设计小而简洁的功能,请使用 接口。如果您正在设计大型功能单元,请使用 抽象类。

          示例是小功能,例如辅助函数或可重用代码块。我最近实现了一个验证provider,它有一个方法Validate,并用于逻辑相关和不相关的具体对象。

          【讨论】:

          • 我会将helper functions 移动到实用程序类,主要是一个不会实现接口的static class
          • 带有你无法实现reusable code block的接口,因为你需要在每个地方提供实现,尽管其中一些是相同的(代码重复)
          • 您的第一点是可以接受的。现在考虑一个场景,您想要验证电话号码和电子邮件。如您所知,这里的验证可能完全不同。所以我的看法是让一个验证方法(接口方法)接受一个验证提供者来验证电子邮件和另一个电话提供者。我希望你明白我在做什么。现在,为了给出更具体的示例,请看这里msdn.microsoft.com/en-us/library/ms972319.aspx,它说明了提供者方法的工作原理。如果您仍然不清楚,请回复我。
          • 用不同的模式验证电话号码/ reg ex 和再次电子邮件。两者都是不同的对象上下文并具有验证行为。所以界面设计在MVC框架中是完美的。但是接口永远不会回答reusable code block。您需要在实施的任何地方编写/复制粘贴
          【解决方案5】:

          大多数时候,当在决定是否应该使用接口或抽象类之间进行讨论时,最终会定义如何使用它们,但并不总是为什么以及何时使用它们?此外,您最终可能也会使用的其他明显的具体类和实用程序类并不总是被提出。真的,在我看来,回答这个问题的正确方法是确定你正在处理的关于域或实体对象的上下文,即你的用例是什么?

          从一个非常高的层次来看,Java 由使用方法相互通信的对象(可以对现实世界中的对象建模的实体或领域对象)组成。无论如何,您希望使用接口对行为进行建模,并在继承时使用抽象类。

          我使用自上而下然后自下而上的方法来执行此操作。我开始通过查看用例并查看我需要哪些类来寻找继承。然后我看看是否有一个包含所有对象的 superClassOrInterfaceType(因为类和接口都定义了类型,为了简单起见,我将它们组合成一个词。希望它不会让它更混乱)域对象,如果我正在处理处理 subtypeClassOrInterfaceTypes 的用例,例如车辆的 superClassOrInterfaceType,例如:汽车、卡车、吉普车和摩托车。如果有层次关系,那么我定义了superClassOrInterfaceType和subtypeClassOrInterfaceTypes。

          正如我所说,我通常首先要做的是为我正在处理的对象寻找一个公共域 superClassOrInterfaceType。如果是这样,我在 subtypeClassOrInterfaceTypes 之间寻找公共方法操作。如果没有,我看看是否有通用方法实现,因为即使您可能有一个 superClassOrInterfaceType 并且可能有通用方法,这些实现可能不利于代码重用。在这一点上,如果我有通用方法,但没有通用实现,我倾向于接口。但是,通过这个简单的示例,我应该在车辆子类型ClassOrInterfaceTypes 之间有一些通用方法和一些通用实现,我可以重用代码。

          另一方面,如果没有继承结构,那我从下往上开始,看看有没有常用的方法。如果没有通用的方法,也没有通用的实现,那我选择具体的类。

          一般来说,如果有通用方法和通用实现的继承,并且在同一个子类型中需要多个子类型实现方法,那么我会选择一个抽象类,这很少见,但我确实使用它。如果只是因为继承而使用抽象类,那么如果代码更改很多,您可能会遇到问题。这在此处的示例中非常详细:Interfaces vs Abstract Classes in Java,用于不同类型的电机域对象。其中一个需要双动力电机,这需要在单个子类型类中使用多种子类型实现方法。

          总而言之,作为一条规则,您希望使用接口而不是抽象类来定义行为(对象将做什么)。抽象类专注于实现层次结构和代码重用。

          这里有一些更详细的链接。

          Thanks Type & Gentle Class

          The Magic behind Subtype Polymorphism

          Maximize Flexibility with Interfaces & Abstract Classes

          Interfaces vs Abstract Classes in Java

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-02-19
            • 2016-03-24
            • 2010-12-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多