Это может быть общий вопрос ООП. Я хотел сделать общее сравнение между интерфейсом и абстрактным классом на основе их использования.
Когда нужно использовать интерфейс, а когда - абстрактный класс ?
Это может быть общий вопрос ООП. Я хотел сделать общее сравнение между интерфейсом и абстрактным классом на основе их использования.
Когда нужно использовать интерфейс, а когда - абстрактный класс ?
Ответы:
Я написал статью об этом:
Абстрактные классы и интерфейсы
Подводя итог:
Когда мы говорим об абстрактных классах, мы определяем характеристики типа объекта; указав, что такое объект .
Когда мы говорим об интерфейсе и определяем возможности, которые мы обещаем предоставить, мы говорим об установлении договора о том, что может сделать объект.
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
. Спасибо
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
Абстрактный класс может иметь общее состояние или функциональность. Интерфейс - это только обещание предоставить состояние или функциональность. Хороший абстрактный класс уменьшит объем кода, который необходимо переписать, потому что его функциональность или состояние могут быть разделены. Интерфейс не имеет определенной информации для обмена
Лично мне почти никогда не нужно писать абстрактные классы.
В большинстве случаев я вижу, что абстрактные классы используются (неправильно), потому что автор абстрактного класса использует шаблон «Шаблонный метод».
Проблема с «Методом шаблона» заключается в том, что он почти всегда несколько повторяется - «производный» класс знает не только о «абстрактном» методе своего базового класса, который он реализует, но и о открытых методах базового класса. , хотя в большинстве случаев это не нужно называть их.
(Чрезмерно упрощенный) пример:
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.
}
}
Проблема в том, что вы неправильно связали воедино две концепции:
В приведенном выше коде теоретически автор метода «сравнение» может повторно вызывать метод «Сортировка» суперкласса ... даже на практике они никогда не захотят или не будут этого делать.
Цена, которую вы платите за эту ненужную связь, состоит в том, что трудно изменить суперкласс, и в большинстве языков 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» ... на функциональном языке программирования этот стиль программирования становится еще более естественным, с меньшим количеством кода.
Если вы смотрите на Java в качестве языка ООП,
« интерфейс не обеспечивает реализацию метода » больше не действует при запуске Java 8. Теперь Java предоставляет реализацию в интерфейсе для методов по умолчанию.
Проще говоря, я хотел бы использовать
интерфейс: для реализации контракта несколькими несвязанными объектами. Он обеспечивает возможность " HAS A ".
абстрактный класс: для реализации одинакового или разного поведения среди нескольких связанных объектов. Он устанавливает отношение " IS A ".
Oracle сайт предоставляет основные различия между interface
и abstract
классом.
Рассмотрите возможность использования абстрактных классов, если:
Рассмотрите возможность использования интерфейсов, если:
Serializable
интерфейс.Пример:
Абстрактный класс ( является соотношение)
Читатель - это абстрактный класс.
BufferedReader являетсяReader
FileReader являетсяReader
FileReader
и BufferedReader
используются для общей цели: чтение данных, и они связаны через Reader
класс.
Интерфейс ( ИМЕЕТ возможность)
Сериализуемый это интерфейс.
Предположим, что у вас есть два класса в вашем приложении, которые реализуют Serializable
интерфейс
Employee implements Serializable
Game implements Serializable
Здесь вы не можете установить какую-либо связь через Serializable
интерфейс между Employee
и Game
, которые предназначены для разных целей. Оба способны сериализировать состояние, и сравнение на этом заканчивается.
Посмотрите на эти сообщения:
Как мне объяснить разницу между интерфейсом и абстрактным классом?
Хорошо, только что сам "проглотил" это - вот это с точки зрения непрофессионала (не стесняйтесь поправлять меня, если я ошибаюсь) - я знаю, что эта тема ооооооооочень, но кто-то другой может наткнуться на нее однажды ...
Абстрактные классы позволяют создавать чертежи и дополнительно КОНСТРУКТИВНО (реализовывать) свойства и методы, которые должны быть доступны ВСЕМ его потомкам.
Интерфейс, с другой стороны, позволяет вам только объявить, что вы хотите, чтобы свойства и / или методы с заданным именем существовали во всех классах, которые его реализуют, но не указывает, как вы должны это реализовать. Кроме того, класс может реализовывать МНОГО интерфейсов, но может расширять только ОДИН абстрактный класс. Интерфейс - это скорее высокоуровневый архитектурный инструмент (который становится понятнее, если вы начнете понимать шаблоны проектирования) - абстрактная работа в обоих лагерях позволяет выполнять некоторые грязные задачи.
Зачем использовать один над другим? Первое допускает более конкретное определение потомков - второе допускает больший полиморфизм . Этот последний пункт важен для конечного пользователя / кодера, который может использовать эту информацию для реализации AP I (интерфейса) в различных комбинациях / формах, чтобы удовлетворить их потребности.
Я думаю, что это был для меня момент «лампочки» - подумайте об интерфейсах меньше с точки зрения автора и больше с точки зрения любого кодера, который придет позже в цепочке, который добавляет реализацию в проект или расширяет API.
Мои два цента:
Интерфейс в основном определяет контракт, которого должен придерживаться любой реализующий класс (реализовать элементы интерфейса). Он не содержит никакого кода.
С другой стороны, абстрактный класс может содержать код, и могут существовать некоторые методы, помеченные как абстрактные, которые должен реализовать наследующий класс.
Редкие ситуации, в которых я использовал абстрактные классы, это когда у меня есть некоторая функциональность по умолчанию, которую наследующий класс может не заинтересовать в переопределении, скажем, абстрактного базового класса, от которого наследуются некоторые специализированные классы.
Пример (очень зачаточном один!): Рассмотрим базовый класс с именем клиента , который имеет абстрактные методы , как CalculatePayment()
, CalculateRewardPoints()
и некоторые не абстрактные методы , как GetName()
, SavePaymentDetails()
.
Специализированные классы , как RegularCustomer
и GoldCustomer
наследует от Customer
базового класса и реализовать свои собственные CalculatePayment()
и CalculateRewardPoints()
логику метода, но повторно использовать GetName()
и SavePaymentDetails()
методы.
Вы можете добавить больше функциональных возможностей к абстрактному классу (т.е. не абстрактным методам), не затрагивая дочерние классы, которые использовали более старую версию. Принимая во внимание, что добавление методов к интерфейсу повлияет на все классы, реализующие его, поскольку теперь им потребуется реализовать вновь добавленные члены интерфейса.
Абстрактный класс со всеми абстрактными членами будет похож на интерфейс.
Когда делать то, что очень просто, если у вас в голове есть ясная концепция.
Абстрактные классы могут быть получены, тогда как интерфейсы могут быть реализованы. Есть некоторая разница между ними. Когда вы наследуете абстрактный класс, отношения между производным классом и базовым классом являются отношениями «это». Например, Собака - это Животное, Овца - это Животное, что означает, что производный класс наследует некоторые свойства от базового класса.
Принимая во внимание, что для реализации интерфейсов, отношение «может быть». например, собака может быть шпионской собакой. Собака может быть цирковой собакой. Собака может быть гоночной собакой. Это означает, что вы реализуете определенные методы для приобретения чего-либо.
Я надеюсь, что я ясно.
1. Если вы создаете что-то, что предоставляет общие функциональные возможности несвязанным классам, используйте интерфейс.
2. Если вы создаете что-то для объектов, тесно связанных в иерархии, используйте абстрактный класс.
Я написал статью о том, когда использовать абстрактный класс, а когда использовать интерфейс. Между ними существует гораздо больше различий, кроме "один IS-A ... и один CAN-DO ...". Для меня это консервированные ответы. Я упоминаю несколько причин, когда использовать любой из них. Надеюсь, поможет.
Я думаю, что самый краткий способ выразить это следующим образом:
Общие свойства => абстрактный класс.
Общая функциональность => интерфейс.
И, если выразиться менее кратко ...
Пример абстрактного класса:
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
сделали звук, но мы еще не знаем его реализацию. В таком случае мы можем абстрагировать реализацию интерфейса и делегировать его реализацию его подклассам.
Еще один момент, помните, как в примере с абстрактным классом мы могли работать с общими свойствами различных объектов, а в примере интерфейса мы могли вызывать общую функциональность различных объектов? В этом последнем примере мы могли бы сделать оба.
Когда предпочитать абстрактный класс интерфейсу?
Когда предпочитать интерфейс над абстрактным классом?
Классы могут наследовать только от одного базового класса, поэтому, если вы хотите использовать абстрактные классы для обеспечения полиморфизма для группы классов, все они должны наследовать от этого класса. Абстрактные классы также могут предоставлять члены, которые уже были реализованы. Следовательно, вы можете обеспечить определенное количество идентичных функциональных возможностей с помощью абстрактного класса, но не можете с помощью интерфейса.
Вот несколько рекомендаций, которые помогут вам решить, использовать ли интерфейс или абстрактный класс для полиморфизма ваших компонентов.
Скопировано с:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
Рассмотрите возможность использования абстрактных классов, если любое из этих утверждений применимо к вашей ситуации:
Рассмотрите возможность использования интерфейсов, если любое из этих утверждений применимо к вашей ситуации:
Ответы различаются в зависимости от языка. Например, в Java класс может реализовывать (наследовать) несколько интерфейсов, но наследовать только от одного абстрактного класса. Таким образом, интерфейсы дают вам больше гибкости. Но это не так в C ++.
Для меня я бы пошел с интерфейсами во многих случаях. Но я предпочитаю абстрактные классы в некоторых случаях.
Классы в ОО вообще относятся к реализации. Я использую абстрактные классы, когда хочу навязать некоторые детали реализации дочерним элементам, иначе я использую интерфейсы.
Конечно, абстрактные классы полезны не только для принудительной реализации, но и для обмена некоторыми конкретными деталями между многими связанными классами.
Используйте абстрактный класс, если вы хотите предоставить некоторые базовые реализации.
в Java вы можете наследовать от одного (абстрактного) класса, чтобы «обеспечить» функциональность, и вы можете реализовать много интерфейсов, чтобы «обеспечить» функциональность
Чисто на основе наследования вы будете использовать реферат, в котором вы определяете явно наследуемые, абстрактные отношения (например, animal-> cat) и / или требуете наследования виртуальных или непубличных свойств, особенно общего состояния (которое интерфейсы не могут поддерживать). ).
Вы должны попытаться отдать предпочтение композиции (посредством внедрения зависимостей), а не наследованию, и заметьте, что интерфейсы, являющиеся контрактами, поддерживают модульное тестирование, разделение проблем и (в зависимости от языка) множественное наследование таким образом, каким не могут абстрагироваться.
Одно интересное место, где интерфейсы работают лучше, чем абстрактные классы, это когда вам нужно добавить дополнительную функциональность в группу (связанных или не связанных) объектов. Если вы не можете дать им базовый абстрактный класс (например, они имеют sealed
или уже имеют родителя), вы можете вместо этого дать им фиктивный (пустой) интерфейс, а затем просто написать методы расширения для этого интерфейса.
Это может быть очень сложный вызов ...
Один указатель, который я могу дать: объект может реализовать много интерфейсов, в то время как объект может наследовать только один базовый класс (в современном языке OO, таком как c #, я знаю, что C ++ имеет множественное наследование - но разве это не осуждается?)
Абстрактный класс может иметь реализации.
Интерфейс не имеет реализаций, он просто определяет вид контракта.
Также могут быть некоторые языковые различия: например, C # не имеет множественного наследования, но в классе может быть реализовано несколько интерфейсов.
Основное правило большого пальца: для "Существительных" используйте абстрактный класс, а для "Глаголов" используйте интерфейс
Например: car
это абстрактный класс, и drive
мы можем сделать его интерфейсом.
drive
в автомобиль - это абстрактный класс.