Когда использовать интерфейс вместо абстрактного класса и наоборот?

Это может быть общий вопрос ООП. Я хотел сделать общее сравнение между интерфейсом и абстрактным классом на основе их использования.

когда нужно использовать интерфейс и когда нужно использовать абстрактный класс?

21 ответов


Я написал об этом статью:

абстрактные классы и интерфейсы

Резюмируя:

когда мы говорим об абстрактных классах мы определяем характеристики Тип объекта; задание что такое объект.

когда мы говорим об интерфейсе и определяем возможности, которые мы обещаем предоставить, мы говорим об установлении контракта о что объект может делать.


абстрактный класс может иметь общее состояние и функциональность. Интерфейс - это только обещание предоставить состояние или функциональность. Хороший абстрактный класс уменьшит количество кода, который нужно переписать, потому что его функциональность или состояние могут быть общими. Интерфейс не имеет определенной информации


лично у меня почти никогда не возникает необходимости писать абстрактные классы.

В большинстве случаев я вижу, что абстрактные классы используются (mis), это потому, что автор абстрактного класса использует шаблон "метод шаблона".

проблема с "шаблонным методом" заключается в том, что он почти всегда несколько повторно поступает - "производный" класс знает не только об "абстрактном" методе своего базового класса, который он реализует, но и об открытых методах базового класса, даже хотя в большинстве случаев не нужно их вызывать.

(чрезмерно упрощенный) пример:

abstract class QuickSorter
{
    public void Sort(object[] items)
    {
        // implementation code that somewhere along the way calls:
        bool less = compare(x,y);
        // ... more implementation code
    }
    abstract bool compare(object lhs, object rhs);
}

Итак, здесь Автор этого класса написал общий алгоритм и намерен использовать его, "специализируя" его, предоставляя свои собственные "крючки" - в этом случае метод "сравнения".

таким образом, предполагаемое использование примерно следующее:

class NameSorter : QuickSorter
{
    public bool compare(object lhs, object rhs)
    {
        // etc.
    }
}

проблема в том, что вы необоснованно соединены два понятия:

  1. способ сравнения двух элементов (какой элемент должен идти первым)
  2. метод сортировки элементов (например, quicksort vs merge sort и т. д.)

В приведенном выше коде, теоретически, автор "сравнить" метод re-entrantly вызовите обратно в суперкласс" Sort " метод... даже если на практике они никогда не захотят или не будут нуждаться в этом.

цена, которую вы платите за это ненужное соединение, заключается в том, что это трудно чтобы изменить суперкласс, и в большинстве языков OO, невозможно изменить его во время выполнения.

альтернативный метод заключается в использовании шаблона проектирования "стратегия" вместо:

interface IComparator
{
    bool compare(object lhs, object rhs);
}

class QuickSorter
{
    private readonly IComparator comparator;
    public QuickSorter(IComparator comparator)
    {
        this.comparator = comparator;
    }

    public void Sort(object[] items)
    {
        // usual code but call comparator.Compare();
    }
}

class NameComparator : IComparator
{
    bool compare(object lhs, object rhs)
    {
        // same code as before;
    }
}

Итак, обратите внимание: все, что у нас есть, это интерфейсы и конкретные реализации этих интерфейсов. На практике вам не нужно ничего другого, чтобы сделать дизайн высокого уровня OO.

чтобы "скрыть" тот факт, что мы реализовали "сортировку имен", используя класс "QuickSort" и a "NameComparator", мы все еще можем написать заводской метод где-нибудь:

ISorter CreateNameSorter()
{
    return new QuickSorter(new NameComparator());
}

любой раз у вас есть абстрактный класс, вы можете сделать это... даже когда между базовым и производным классом существует естественная связь повторного входа, обычно стоит сделать их явными.

одна последняя мысль: все, что мы сделали выше, это "составить" функцию "NameSorting", используя функцию "QuickSort" и функцию "NameComparison"... на функциональном языке программирования, этот стиль программирования становится еще более естественным, с меньшим количеством кода.


хорошо, просто "grokked" это сам-вот это в терминах непрофессионала (не стесняйтесь поправлять меня, если я ошибаюсь) - Я знаю, что эта тема oooooold, но кто-то другой может наткнуться на нее однажды...

абстрактные классы позволяют создавать чертежи и дополнительно создавать (реализовывать) свойства и методы, которыми должны обладать все его потомки.

интерфейс, с другой стороны, позволяет только объявить, что вы хотите свойства и / или методы с заданным именем существуют во всех классах, которые его реализуют , но не указывают, как его реализовать. Кроме того, класс может реализовать много интерфейсов, но может расширить только один абстрактный класс. Интерфейс - это скорее архитектурный инструмент высокого уровня (который становится понятнее, если вы начинаете понимать шаблоны дизайна) - Абстракт имеет ногу в обоих лагерях и может выполнять некоторую грязную работу.

зачем использовать одно над другим? Бывший позволяет более б определение потомков-последнее позволяет больше полиморфизм. Этот последний момент важен для конечного пользователя / кодера, который может использовать эту информацию для реализации A. P.I (nterface) в различных комбинациях / формах в соответствии с их потребностями.

Я думаю, что это был момент "лампочки" для меня-подумайте об интерфейсах меньше от перспективы автора и больше от любого кодера, приходящего позже в цепочке, который добавление реализации в проект или расширения API-интерфейс.


мои две копейки:

интерфейс в основном определяет контракт, к которому должен придерживаться любой класс реализации(реализовать члены интерфейса). Он не содержит никакого кода.

С другой стороны, абстрактный класс может содержать код, и могут быть некоторые методы, помеченные как абстрактные, которые должен реализовать наследующий класс.

редкие ситуации, в которых я использовал абстрактные классы, - это когда у меня есть некоторые функции по умолчанию, которые наследующий класс может быть неинтересно переопределять, скажем, абстрактный базовый класс, который наследуют некоторые специализированные классы.

пример(очень элементарная!): Рассмотрим базовый класс Customer, который имеет абстрактные методы, такие как CalculatePayment(), CalculateRewardPoints() и некоторые не абстрактные методы, такие как GetName(), SavePaymentDetails().

специализированные классы, такие как RegularCustomer и GoldCustomer наследует от Customer базовый класс и реализовать собственные CalculatePayment() и CalculateRewardPoints() логика метода, но повторно используйте GetName() и SavePaymentDetails() методы.

вы можете добавить больше функциональности в абстрактный класс (не абстрактные методы), не затрагивая дочерние классы, которые использовали более старую версию. В то время как добавление методов к интерфейсу повлияет на все классы, реализующие его, поскольку теперь им нужно будет реализовать новые добавленные члены интерфейса.

абстрактный класс со всеми абстрактными членами будет похож на интерфейс.


когда делать то, что очень просто, если у вас есть концепция ясно в вашем уме.

абстрактные классы могут быть производными, тогда как интерфейсы могут быть реализованы. Между ними есть некоторая разница. Когда вы производите абстрактный класс, отношение между производным классом и базовым классом является отношением "is a". например, собака-животное, овца-животное, что означает, что производный класс наследует некоторые свойства из базы класс.

тогда как для реализации интерфейсов отношение "может быть". например, собака может быть собакой-шпионом. Собака может быть цирковой собакой. Собака может быть гоночной собакой. Это означает, что вы реализуете определенные методы, чтобы что-то приобрести.

надеюсь, я ясно.


если вы смотрите на java как язык ООП,

"интерфейс не обеспечивает реализацию метода " больше не действует при запуске Java 8. Теперь java предоставляет реализацию в интерфейсе для методов по умолчанию.

проще говоря, я хотел бы использовать

интерфейс: для реализации контракта несколькими несвязанными объектами. Он обеспечивает"ЕСТЬ" возможности.

абстрактный класс: реализовать то же или другое поведение среди нескольких связанных объектов. Он устанавливает "ЭТО" связь.

Oracle сайт обеспечивает ключевые различия между interface и abstract класса.

рассмотрите возможность использования абстрактных классов если :

  1. вы хотите поделиться кодом среди нескольких тесно связанных занятия.
  2. вы ожидаете, что классы, расширяющие абстрактный класс, имеют много общих методов или полей или требуют модификаторов доступа, отличных от public (например, protected и private).
  3. вы хотите объявить нестатические или не конечные поля.

рассмотрите возможность использования интерфейсов если :

  1. вы ожидаете, что несвязанные классы будут реализовывать ваш интерфейс. Например, многие несвязанные объекты могут реализовать Serializable взаимодействие.
  2. вы хотите указать поведение определенного типа данных, но не заботитесь о том, кто реализует его поведение.
  3. вы хотите воспользоваться преимуществами множественного наследования типа.

пример:

абстрактный класс ( ЭТО связи)

читатель является абстрактным классом.

командой bufferedreader - это Reader

FileReader это Reader

FileReader и BufferedReader используются для общей цели : чтение данных, и они связаны через Reader класса.

интерфейс ( ЕСТЬ возможности )

сериализуемые - это интерфейс.

предположим, что есть два класса в вашем приложении, которые реализуют Serializable интерфейс

Employee implements Serializable

Game implements Serializable

здесь вы не можете установить какие-либо отношения через Serializable интерфейс между Employee и Game, которые предназначены для разных целей. Оба они способны сериализовать состояние, и на этом сравнение заканчивается.

посмотрите на эти сообщения:

как я должен был объяснить разницу между интерфейсом и абстрактным классом?


Я написал статью о том, когда использовать абстрактный класс и интерфейс. Между ними гораздо больше различий, чем между "один-а"... а можно-сделать...". Для меня это законсервированные ответы. Я упоминаю несколько причин, когда использовать любой из них. Надеюсь, это поможет.

http://codeofdoom.com/wordpress/2009/02/12/learn-this-when-to-use-an-abstract-class-and-an-interface/


1.Если вы создаете что-то, что предоставляет общую функциональность несвязанным классам, используйте интерфейс.

2.Если вы создаете что-то для объектов, тесно связанных в иерархии, используйте абстрактный класс.


классы могут наследовать только от одного базового класса, поэтому, если вы хотите использовать абстрактные классы для предоставления полиморфизма группе классов, все они должны наследовать от этого класса. Абстрактные классы могут также предоставлять элементы, которые уже реализованы. Таким образом можно обеспечить определенное количество идентичных функций с абстрактным классом, но нельзя с интерфейсом.

вот некоторые рекомендации, которые помогут вам решить, использовать ли интерфейс или абстрактный класс для обеспечения полиморфизма для ваших компонентов.

  • если предполагается создание нескольких версий компонента, создайте абстрактный класс. Абстрактные классы обеспечивают простой и легкий способ версии ваших компонентов. При обновлении базового класса все наследующие классы автоматически обновляются с изменением. Интерфейсы, с другой стороны, не могут быть изменены после создания таким образом. Если требуется новая версия интерфейса, необходимо создать целое новый интерфейс.
  • если создаваемая Вами функциональность будет полезна для широкого спектра разрозненных объектов, используйте интерфейс. Абстрактные классы должны использоваться главным образом для тесно связанных объектов, в то время как интерфейсы лучше всего подходят для предоставления общих функций несвязанным классам.
  • Если вы разрабатываете небольшие, краткие биты функциональности, используйте интерфейсы. Если вы разрабатываете большие функциональные единицы, используйте абстрактный класс.
  • если вы хотите обеспечить общую, реализованную функциональность среди всех реализаций вашего компонента, используйте абстрактный класс. Абстрактные классы позволяют частично реализовать класс, В то время как интерфейсы не содержат реализации для каких-либо членов.

скопировать из:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx


рассмотрите возможность использования абстрактные классы Если какое-либо из этих утверждений применимо к вашей ситуации:

  1. вы хотите поделиться кодом между несколькими тесно связанными классами.
  2. вы ожидаете, что классы, расширяющие абстрактный класс, имеют много общих методов или полей или требуют модификаторов доступа, отличных от public (например, protected и private).
  3. вы хотите объявить нестатические или не конечные поля. Это позволяет определить методы, которые могут доступ и изменение состояния объекта, к которому они принадлежат.

рассмотрите возможность использования интерфейсы Если какое-либо из этих утверждений применимо к вашей ситуации:

  1. вы ожидаете, что несвязанные классы будут реализовывать ваш интерфейс. Например, интерфейсы Comparable и Cloneable реализуются многими несвязанными классами.
  2. вы хотите указать поведение определенного типа данных, но не беспокоитесь о том, кто реализует его поведение.
  3. вы хотите воспользоваться преимуществами нескольких наследств.

источник


мне кажется, наиболее емко сказано следующее:

общие свойства => абстрактный класс.
Общая функциональность => интерфейс.

и выражаясь менее лаконично...

Пример Абстрактного Класса:

public abstract class BaseAnimal
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }
}

public class Dog : BaseAnimal
{
    public Dog() : base(4) { }
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }
}

поскольку животные имеют общее свойство-количество ног в этом случае-имеет смысл сделать абстрактный класс, содержащий это общее свойство. Это также позволяет нам писать общий код, который работает на этом участке. Например:

public static int CountAllLegs(List<BaseAnimal> animals)
{
    int legCount = 0;
    foreach (BaseAnimal animal in animals)
    {
        legCount += animal.NumberOfLegs;
    }
    return legCount;
}

Интерфейс Пример:

public interface IMakeSound
{
    void MakeSound();
}

public class Car : IMakeSound
{
    public void MakeSound() => Console.WriteLine("Vroom!");
}

public class Vuvuzela : IMakeSound
{
    public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");        
}

обратите внимание, что Vuvuzelas и автомобили-это совершенно разные вещи, но они имеют общую функциональность: создание звука. Таким образом, интерфейс имеет смысл здесь. Кроме того, это позволит программистам группировать вещи, которые делают звуки вместе под общим интерфейсом -- IMakeSound в этом случае. С этой конструкцией, вы смогли написать следующее код:

List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());

foreach (IMakeSound soundMaker in soundMakers)
{
    soundMaker.MakeSound();
}

можете ли вы сказать, что это выведет?

наконец, вы можете объединить два.

Комбинированного Пример:

public interface IMakeSound
{
    void MakeSound();
}

public abstract class BaseAnimal : IMakeSound
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }

    public abstract void MakeSound();
}

public class Cat : BaseAnimal
{
    public Cat() : base(4) { }

    public override void MakeSound() => Console.WriteLine("Meow!");
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }

    public override void MakeSound() => Console.WriteLine("Hello, world!");
}

здесь, мы требуем все BaseAnimals издает звук,но мы еще не знаем его реализации. В таком случае мы можем абстрагировать реализацию интерфейса и делегировать ее реализацию своим подклассам.

последний момент, помните, как в примере абстрактного класса мы смогли работать с общими свойствами разных объектов и в Примере интерфейса мы смогли вызвать общую функциональность разных объектов? В последнем примере мы могли бы сделать и то, и другое.


ответы варьируются между языками. Например, в Java класс может реализовать (наследовать) несколько интерфейсов, но наследовать только от одного абстрактного класса. Так интерфейсы дают вам больше гибкости. Но это не так в C++.


для меня я бы пошел с интерфейсами во многих случаях. Но в некоторых случаях я предпочитаю абстрактные классы.

классы в OO в целом относятся к реализации. Я использую абстрактные классы, когда хочу заставить некоторые детали реализации для детей, иначе я иду с интерфейсами.

конечно, абстрактные классы полезны не только для принудительной реализации, но и для обмена некоторыми конкретными деталями среди многих связанных классов.


используйте абстрактный класс, если вы хотите предоставить некоторые основные реализации.


в java вы можете наследовать от одного (абстрактного) класса для" предоставления "функциональности, и вы можете реализовать много интерфейсов для" обеспечения " функциональности


чисто на основе наследования вы бы использовали абстрактное, где вы определяете явно потомок, абстрактные отношения (т. е. животное->кошка) и/или требуете наследования виртуальных или непубличных свойств, особенно общего состояния (которое интерфейсы не могут поддерживать).

вы должны попытаться выбрать композицию (через инъекцию зависимостей) над наследованием, где вы можете, и обратите внимание, что интерфейсы, являющиеся контрактами, поддерживают модульное тестирование, разделение проблемы и (язык меняется) множественное наследование таким образом, что абстракции не могут.


одно интересное место, где интерфейсы лучше, чем абстрактные классы, - это когда вам нужно добавить дополнительную функциональность в группу (связанных или несвязанных) объектов. Если вы не можете дать им базовый абстрактный класс (например, они sealed или уже есть родитель), вы можете дать им фиктивный (пустой) интерфейс, а затем просто написать методы расширения для этого интерфейса.


Это может быть очень трудно сделать...

один указатель, который я могу дать: объект может реализовать много интерфейсов, в то время как объект может наследовать только один базовый класс( В современном языке OO, таком как c#, я знаю, что C++ имеет множественное наследование - но разве это не нахмурилось?)


абстрактный класс может иметь реализации.

интерфейс не имеет реализаций, он просто определяет вид контракта.

также могут быть некоторые зависящие от языка различия: например, C# не имеет множественного наследования, но несколько интерфейсов могут быть реализованы в классе.


основное правило большого пальца: для "существительные" использовать абстрактные класс а "глаголы" использовать интерфейс

например: car является абстрактным классом и drive, мы можем сделать его интерфейс.