【问题标题】:(Regular Class + interface) vs Abstract class(常规类+接口)vs抽象类
【发布时间】:2018-01-14 11:08:58
【问题描述】:

我最近遇到一个关于抽象类的问题。

抽象类的功能可以通过使用(带有受保护构造函数的常规类+接口)的组合来实现。

使用抽象类比(具有受保护构造函数+接口的常规类)有什么好处。

恕我直言,抽象类的目的是具有需要在整个类层次结构中可用的通用功能。它可以限制子类以通过抽象方法实现某些功能。它可以允许子类覆盖常见的行为。

抽象类不用作具体对象。所以,它不允许实例化抽象类。

但是,我们可以使用常规类+接口来实现同样的事情。

  1. 将常规类构造函数标记为受保护,因此不能单独创建对象
  2. 提供常用功能的默认实现并将它们标记为虚拟,以防它们需要被子类覆盖。
  3. 使用接口强制子类实现某些功能。

那么,Abstract 类是否提供了任何额外的功能?

我想不出其他的了。面试官试图了解抽象类与具有受保护构造函数 + 接口的常规类相比还有哪些其他好处。

【问题讨论】:

  • 请不要无故投反对票。我想了解其中的原理。如果这不是正确的问题,我会删除它。
  • 普通类可以创建自己的实例,抽象类不能。
  • 另外,如果你给一个类添加一个接口,你必须在那个类上实现它,你不能离开实现抽象。所以你的常规类+接口必须实现接口才能编译。为什么要跳过所有这些障碍来实现已经存在的东西?
  • 数字 3 是错误的 - 你不能强制一个类既继承自实现接口的类强制它自己实现所有接口方法。
  • 您的建议无论如何都不是一个真正的选择。从您的基类继承的类根本不需要实现接口方法。事实上,除非基类将它们标记为虚拟,否则它甚至无法虚拟覆盖它们。那么在这个场景中,界面的意义何在?这种比较实际上是在抽象类和具有受保护构造函数的常规类之间进行的,它不能作为抽象类的替代品。

标签: c# oop inheritance abstract-class


【解决方案1】:

很多很好的理由。让我们从一个明确的开始:

public abstract class Smell
{
    public abstract string GetAdjective();
    public string GetDescription()
    {
        return "I smell " + GetAdjective();
    }
}
public class NastySmell : Smell
{
    public override string GetAdjective() { return "really nasty"; }
}

很简单。抽象类有一个函数 GetDescription - 它依赖于抽象方法 GetAdjective 的存在。

如何使用 ProtectedConstructor+Interface 做到这一点?您不能让 Smell 实现接口(有很多原因,但一个重要的原因是任何派生类也将继承实现,并且不需要实现任何新的东西)——但是表示它的函数不能引用方法:

public interface SmellInterface
{
    string GetAdjective();
}
public class Smell
{
    protected Smell() { }
    public string GetDescription()
    {
        // how do I call GetAdjective here?  I have no reference to it!
    }
}

但还有另一个更令人信服的理由:

public abstract class SomeFancyClass
{
    protected string name;
    protected string server;
    protected abstract string implementer { get; }
    public string Generate()
    {
        if (name == "something")
            HandleGlobally(name);
        else
            HandleSpecifically(name);
    }
    public void HandleGlobally(string server)
    {
        // code
    }
    public abstract void HandleSpecifically(string server);
}

...如果您将此类设为 ProtectedConstructorClass + Interface 的组合,您会将代码分成两个单独的位置 - 突然之间,您必须查看两半才能了解正在发生的事情的全貌!

public interface AbstractHalf
{
    // data property of 'implementer'
    // method of 'HandleSpecifically()
}
public class NonabstractHalf
{
    // data fields of 'name' and 'server'
    // methods of 'Generate()' and 'HandleGlobally'
}

...你为什么要这样做?您的班级是一个独特的逻辑实体。你为什么要把它分成两个独立的部分:非抽象与抽象?它只会使阅读和故障排除变得更加困难。更糟糕的是,类中的代码和抽象声明越多。

【讨论】:

    【解决方案2】:

    抽象类的主要好处是强制开发人员创建一个继承自抽象类的子类,以便使用基/共享功能和字段。

    你不能直接新建一个抽象类。您可以新建一个常规类 + 接口,而不必强制继承或覆盖基础中的任何内容。

    使用抽象类,您可以减少文件的数量 - 即没有接口,但大多数人可能希望保留那些用于向 IoC 容器注册和依赖注入的文件。

    【讨论】:

      【解决方案3】:

      我能想到的一件事是,通过使用抽象类,您可以简单地通过不将方法或属性标记为virtual 来强制执行特定的实现,而使用接口则不能阻止类实现接口但不是从您的基类派生的。

      使用抽象类的另一个好处是,您可以简单地向抽象类添加功能,而不必担心所有派生类的实现 - 同样,因为您无法阻止类在不派生的情况下实现接口你的基类。

      此外,抽象类可以具有受保护的字段、方法、事件等,但接口不能。

      这一切都归结为您不能强制实现您的接口的类从您的“常规”基类派生。

      【讨论】:

        【解决方案4】:

        首先,有很多关于抽象类和接口之间差异的问题和答案,例如:this。有很多非凡的答案。但大部分都是关于编程和语法的。

        我想从设计的角度来看:

        我认为抽象类不能起到接口的作用(+正则类) 在软件设计。

        抽象类:
        抽象类的主要目标是抽象原理。为了克服这种复杂性,抽​​象类用于在外观相似的类中创建层次结构。层次结构中的所有类都在扩展基类的功能并扩展基类的类型。

        界面:
        但是,接口用于类之间的交互。这些类可以相似也可以不相似。它们可以来自不同的层次结构和不同的类型。

        另外,它们是继承类(甚至是抽象类)和实现接口之间的巨大区别。接口不是TYPES。它们是共享边界,计算机系统的两个或多个独立组件在它们之间交换信息。

        【讨论】:

          猜你喜欢
          • 2010-10-29
          • 2014-10-09
          • 1970-01-01
          • 2011-05-22
          • 2017-02-19
          • 1970-01-01
          • 2014-04-12
          • 2010-12-01
          • 1970-01-01
          相关资源
          最近更新 更多