我最近接受了两次电话采访,被问及接口类和抽象类之间的区别。我已经解释了我能想到的每一个方面,但似乎他们在等我提一些具体的事情,我不知道是什么。
根据我的经验,我认为以下是正确的。如果我遗漏了一个要点,请告诉我。
接口:
接口中声明的每个方法都必须在子类中实现。接口中只能存在事件、委托、财产(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?
默认情况下,接口中的变量是公共静态变量和最终变量。问题的框架可以是,如果抽象类中的所有变量都是公共的呢?与接口中的变量不同,它们仍然可以是非静态和非最终的。
最后,我想对上面提到的内容再补充一点——抽象类仍然是类,属于单个继承树,而接口可以存在于多个继承中。