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

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

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

Ответы

Ответ 1

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

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

Подведение итогов:

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

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

Ответ 2

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

Ответ 3

Лично мне почти не нужно писать абстрактные классы.

В большинстве случаев я вижу абстрактные классы, которые используются (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.
    }
}

Проблема заключается в том, что вы неправильно связали две концепции:

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

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

Цена, которую вы платите за эту ненужную связь, заключается в том, что трудно изменить суперкласс, а на большинстве языков 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" и "NameComparator", мы все равно можем написать метод factory где-то:

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

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

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

Ответ 4

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

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

С другой стороны, интерфейс позволяет объявлять, что вы хотите, чтобы свойства и/или методы с заданным именем существовали во всех классах, которые его реализуют, но не указывают, как вы должны его реализовать. Кроме того, класс может реализовывать МНОГИЕ интерфейсы, но может только расширить класс ONE Abstract. Интерфейс - это скорее архитектурный инструмент высокого уровня (который становится понятнее, если вы начинаете понимать шаблоны проектирования). У абстрактного есть нога в обоих лагерях и может выполнять некоторые из грязных работ.

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

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

Ответ 5

Мои два цента:

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

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

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

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

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

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

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

Ответ 6

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

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

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

Надеюсь, я поняла.

Ответ 7

Если вы смотрите на Java как язык OOP,

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

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

интерфейс. Чтобы реализовать контракт несколькими несвязанными объектами. Он обеспечивает " HAS A".

абстрактный класс: Чтобы реализовать одно и то же или другое поведение среди нескольких связанных объектов. Он устанавливает отношение IS A.

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

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

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

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

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

Пример:

Абстрактный класс (отношение IS A)

Reader - абстрактный класс.

BufferedReader - это Reader

FileReader - это Reader

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

Интерфейс (HAS A)

Serializable - это интерфейс.

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

Employee implements Serializable

Game implements Serializable

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

Взгляните на эти сообщения:

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

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

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

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

Ответ 11

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

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

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

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

Источник

Ответ 12

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

Ответ 13

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

Ответ 14

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

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

Ответ 15

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

Ответ 16

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

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

И, чтобы выразить это менее кратко...

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

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 и Cars - это совершенно разные вещи, но у них есть общая функциональность: создание звука. Таким образом, интерфейс здесь имеет смысл. Кроме того, это позволит программистам группировать вещи, которые создают звуки вместе под общим интерфейсом - 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!");
}

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

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

Ответ 17

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

Ответ 18

Это может быть очень сложный вызов, чтобы сделать...

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

Ответ 19

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

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

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

Ответ 20

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

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

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

Ответ 21

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

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