Когда использовать интерфейс вместо абстрактного класса и наоборот? - PullRequest
390 голосов
/ 26 января 2009

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

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

Ответы [ 22 ]

401 голосов
/ 26 января 2009

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

388 голосов
/ 26 января 2009

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

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

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

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

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

79 голосов
/ 26 января 2009

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

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

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

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

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. Метод сортировки элементов (т.е. быстрая сортировка или сортировка слиянием и т. Д.)

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

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

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

Чтобы «скрыть» тот факт, что мы реализовали «сортировку имен» с использованием класса «QuickSort» и «NameComparator», мы все равно могли бы написать где-нибудь фабричный метод:

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

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

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

39 голосов
/ 12 июля 2009

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

Абстрактные классы позволяют создавать чертежи и позволяют дополнительно СОСТАВЛЯТЬ (реализовывать) свойства и методы, которыми должны обладать ВСЕ его потомки.

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

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

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

37 голосов
/ 07 декабря 2010

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

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

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

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

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

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

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

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

36 голосов
/ 27 ноября 2015

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

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

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

интерфейс: Для реализации контракта несколькими несвязанными объектами. Он обеспечивает возможность « HAS A ».

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

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

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

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

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

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

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

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

BufferedReader является Reader

FileReader является Reader

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

Интерфейс ( ИМЕЕТ A возможность)

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

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

Employee implements Serializable

Game implements Serializable

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

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

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

29 голосов
/ 26 января 2009

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

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

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

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

10 голосов
/ 28 марта 2013

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

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

10 голосов
/ 12 февраля 2009

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

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

6 голосов
/ 14 июня 2017

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

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

И, говоря менее кратко ...

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

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 издавали звук, но мы еще не знаем его реализацию. В таком случае мы можем абстрагировать реализацию интерфейса и делегировать его реализацию его подклассам.

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

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