我最近接受了两次电话采访,被问及接口类和抽象类之间的区别。我已经解释了我能想到的每一个方面,但似乎他们在等我提一些具体的事情,我不知道是什么。

根据我的经验,我认为以下是正确的。如果我遗漏了一个要点,请告诉我。

接口:

接口中声明的每个方法都必须在子类中实现。接口中只能存在事件、委托、财产(C#)和方法。一个类可以实现多个接口。

抽象类:

子类只能实现抽象方法。抽象类可以具有具有实现的普通方法。除了事件、委托、财产和方法之外,抽象类还可以有类变量。由于C#中不存在多重继承,一个类只能实现一个抽象类。

在这之后,面试官提出了一个问题:“如果你有一个只有抽象方法的抽象类呢?这和接口有什么不同?”我不知道答案,但我认为这是上面提到的继承,对吧?另一位面试官问我,“如果你在接口中有一个公共变量,那会和抽象类中有什么不同?”我坚持认为你不能在接口中使用公共变量。我不知道他想听什么,但他也不满意。

另请参阅:

何时使用接口而不是抽象类,反之亦然接口与抽象类如何决定使用抽象类和接口?接口和抽象类之间的区别是什么?


当前回答

抽象类处理有效地打包类功能,而接口用于意图/契约/通信,并且应该与其他类/模块共享。

使用抽象类作为契约和(部分)契约实现者违反了SRP。使用抽象类作为契约(依赖关系)限制了创建多个抽象类以获得更好的重用性。

在下面的示例中,使用抽象类作为OrderManager的合同会产生问题,因为我们有两种不同的处理订单的方式-基于客户类型和类别(客户可以是直接或间接的,也可以是黄金或白银)。因此,接口用于契约,抽象类用于不同的工作流实施

public interface IOrderProcessor
{
    bool Process(string orderNumber);
}

public abstract class CustomerTypeOrderProcessor: IOrderProcessor
{
    public bool Process(string orderNumber) => IsValid(orderNumber) ? ProcessOrder(orderNumber) : false;

    protected abstract bool ProcessOrder(string orderNumber);

    protected abstract bool IsValid(string orderNumber);
}

public class DirectCustomerOrderProcessor : CustomerTypeOrderProcessor
{
    protected override bool IsValid(string orderNumber) => string.IsNullOrEmpty(orderNumber); 

    protected override bool ProcessOrder(string orderNumber) => true; 
}

public class InDirectCustomerOrderProcessor : CustomerTypeOrderProcessor
{
    protected override bool IsValid(string orderNumber) => orderNumber.StartsWith("EX");

    protected override bool ProcessOrder(string orderNumber) => true;
}

public abstract class CustomerCategoryOrderProcessor : IOrderProcessor
{
    public bool Process(string orderNumber) => ProcessOrder(GetDiscountPercentile(orderNumber), orderNumber);

    protected abstract int GetDiscountPercentile(string orderNumber);

    protected abstract bool ProcessOrder(int discount, string orderNumber);
}

public class GoldCustomer : CustomerCategoryOrderProcessor
{
    protected override int GetDiscountPercentile(string orderNumber) => 15;

    protected override bool ProcessOrder(int discount, string orderNumber) => true;

}

public class SilverCustomer : CustomerCategoryOrderProcessor
{
    protected override int GetDiscountPercentile(string orderNumber) => 10;

    protected override bool ProcessOrder(int discount, string orderNumber) => true;

}

public class OrderManager
{
    private readonly IOrderProcessor _orderProcessor;// Not CustomerTypeOrderProcessor or CustomerCategoryOrderProcessor 

    //Using abstract class here would create problem as we have two different abstract classes
    public OrderManager(IOrderProcessor orderProcessor) => _orderProcessor = orderProcessor;
}

其他回答

从概念上讲,保持特定于语言的实现、规则、好处,并通过使用任何人或两者实现任何编程目标,可以或不可以有代码/数据/属性等等,单继承或多继承等等

1-抽象(或纯抽象)类旨在实现层次结构。如果您的业务对象在结构上看起来有些相似,仅表示父子(层次结构)类型的关系,那么将使用继承/抽象类。如果您的业务模型没有层次结构,那么就不应该使用继承(这里我不是在谈论编程逻辑,例如一些设计模式需要继承)。从概念上讲,抽象类是一种在OOP中实现业务模型层次结构的方法,它与接口无关,实际上将抽象类与接口进行比较是没有意义的,因为两者在概念上完全不同,在访谈中要求它只是为了检查概念,因为当涉及到实现时,它看起来都提供了一些相同的功能,而我们程序员通常更强调编码。[请记住,抽象与抽象类不同]。

2-接口是一个契约,一个由一组或多组功能表示的完整业务功能。这就是它被实现而不是继承的原因。业务对象(是否是层次结构的一部分)可以具有任意数量的完整业务功能。它与抽象类无关,通常意味着继承。例如,人可以跑步,大象可以跑步,鸟可以跑步等等,所有这些不同层次的对象都将实现RUN接口或EAT或SPEAK接口。不要进入实现,因为您可能会将其实现为为实现这些接口的每种类型提供抽象类。任何层次结构的对象都可以具有与其层次结构无关的功能(接口)。

我相信,接口的发明并不是为了实现多重继承或公开公共行为,类似地,纯抽象类并不是为了推翻接口,而是接口是一个对象可以实现的功能(通过接口的功能),抽象类代表一个层次结构的父级,以生成具有父级核心结构(属性+功能)的子级

当你被问到差异时,这实际上是概念上的差异,而不是特定语言实现中的差异,除非被明确问到。

我相信,两位面试官都希望这两者之间有一条直线的直接区别,当你失败时,他们试图通过将“一个作为另一个”来驱使你实现这一区别

如果你有一个只有抽象方法的抽象类呢?

After all that, the interviewer came up with the question "What if you had an 
Abstract class with only abstract methods? How would that be different
from an interface?" 

文档明确指出,如果抽象类只包含抽象方法声明,则应将其声明为接口。

An another interviewer asked me what if you had a Public variable inside
the interface, how would that be different than in Abstract Class?

默认情况下,接口中的变量是公共静态变量和最终变量。问题的框架可以是,如果抽象类中的所有变量都是公共的呢?与接口中的变量不同,它们仍然可以是非静态和非最终的。

最后,我想对上面提到的内容再补充一点——抽象类仍然是类,属于单个继承树,而接口可以存在于多个继承中。

接口是执行特定行为的轻量级方式。这是一种想法。

我认为他们不喜欢你的回应,因为你给出的是技术上的差异,而不是设计上的差异。对我来说,这个问题就像一个巨魔问题。事实上,接口和抽象类有着完全不同的性质,所以你无法真正比较它们。我将向您介绍接口的作用和抽象类的作用。

接口:用于确保契约和类之间的低耦合,以获得更可维护、可扩展和可测试的应用程序。

抽象类:仅用于在具有相同响应性的类之间分解某些代码。请注意,这是为什么多重继承在OOP中是一件坏事的主要原因,因为类不应该处理许多响应(而是使用组合)。

因此,接口具有真正的体系结构角色,而抽象类几乎只是实现的一个细节(当然,如果正确使用的话)。

抽象类不是纯抽象的,因为它是具体(实现的方法)和未实现的方法的集合。但是接口是纯抽象的,因为只有未实现的方法,而没有具体的方法。

还有一个区别是

抽象类允许您创建子类可以实现或覆盖的功能。接口只允许您定义功能,而不允许实现它。

何时使用抽象类

如果用户希望为所有对象编写通用功能。抽象类是未来重新实现的最佳选择,它可以在不影响最终用户的情况下添加更多功能。

何时使用接口

如果用户希望编写不同的功能,则对象上的功能将不同。接口是最好的选择,如果接口发布后不需要修改需求。