Абстрактный класс имеет дело с эффективной упаковкой функциональности класса, тогда как интерфейс предназначен для намерения / контракта / связи и должен использоваться совместно с другими классами / модулями.
Использование абстрактных классов в качестве исполнителя контракта и (частичного) контракта нарушает 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;
}