Интерфейс или абстрактный класс: какой из них использовать?

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

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

Solutions Collecting From Web of "Интерфейс или абстрактный класс: какой из них использовать?"

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

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

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

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

Разница между Abstract Class и Interface

Абстрактные классы

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

  • Выведенный класс может или не может переопределять конкретные функции, определенные в базовом классе

  • Детский класс, расширенный от абстрактного класса, должен быть логически связан

Интерфейс

Интерфейс не может содержать никаких функций . Он содержит только определения методов

  • Полученный класс ДОЛЖЕН предоставить код для всех методов, определенных в интерфейсе

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

Зачем использовать абстрактные классы? Ниже приведен простой пример. Допустим, у нас есть следующий код:

 <?php class Fruit { private $color; public function eat() { // chew } public function setColor($c) { $this->color = $c; } } class Apple extends Fruit { public function eat() { // chew until core } } class Orange extends Fruit { public function eat() { // peeling // chew } } 

Теперь я даю вам яблоко, и вы едите его. Как это на вкус? На вкус это яблоко.

 <?php $apple = new Apple(); $apple->eat(); // Now I give you a fruit. $fruit = new Fruit(); $fruit->eat(); 

Что это такое? Ну, это не имеет большого смысла, поэтому вы не должны этого делать. Это достигается путем создания абстрактного класса Fruit, а также метода съедания внутри него.

 <?php abstract class Fruit { private $color; abstract public function eat(){} public function setColor($c) { $this->color = $c; } } ?> 

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

Пример реального мира:

 <?php abstract class person { public $LastName; public $FirstName; public $BirthDate; abstract protected function write_info(); } final class employee extends person{ public $EmployeeNumber; public $DateHired; public function write_info(){ //sql codes here echo "Writing ". $this->LastName . "'s info to emloyee dbase table <br>"; } } final class student extends person{ public $StudentNumber; public $CourseName; public function write_info(){ //sql codes here echo "Writing ". $this->LastName . "'s info to student dbase table <br>"; } } ///---------- $personA = new employee; $personB = new student; $personA->FirstName="Joe"; $personA->LastName="Sbody"; $personB->FirstName="Ben"; $personB->LastName="Dover"; $personA->write_info(); // Writing Sbody's info to emloyee dbase table $personB->write_info(); // Writing Dover's info to student dbase table 

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

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

Например:

 <?php class parser implements parserDecoratorPattern { //... } 

Таким образом, любой, кто читает мой код (и кто знает, что представляет собой шаблон Decorator), сразу узнает: а) как я создаю свой парсер и б) уметь видеть, какие методы используются для реализации шаблона декоратора.

Кроме того, я могу быть здесь не в качестве программиста Java / C ++ / etc, но типы данных могут вступить в игру здесь. Ваши объекты имеют тип, и когда вы передаете их по типу, программно. Перемещение ваших поддающихся заключению элементов в интерфейс только диктует типы, возвращаемые методами, но не базовый тип класса, который его реализует.

Уже поздно, и я не могу придумать лучший пример psudo-кода, но здесь говорится:

 <?php interface TelevisionControls {}; class Remote implements TelevisionControls {}; class Spouse implements TelevisionControls {}; Spouse spouse = new Spouse(); Remote remote = new Remote(); isSameType = (bool)(remote == spouse) 

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

Интерфейс – это контракт поведения без какой-либо реализации.

Чтобы добавить к некоторым из уже отличных ответов:

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

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

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

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

  • класс может расширять суперкласс и реализовывать любое количество интерфейсов:

     class SubClass extends ParentClass implements Interface1, Interface2 { // ... } 

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

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

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

 <?php // interface class X implements Y { } // this is saying that "X" agrees to speak language "Y" with your code. // abstract class class X extends Y { } // this is saying that "X" is going to complete the partial class "Y". ?> 

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

Вот упрощенный пример / пример. Извлеките все детали реализации. Например, измените свой абстрактный класс:

 abstract class ClassToBuildUpon { public function doSomething() { echo 'Did something.'; } } 

чтобы:

 interface ClassToBuildUpon { public function doSomething(); } 

С философской точки зрения:

  • Абстрактный класс представляет собой «отношение». Допустим, у меня есть плоды, и у меня бы был класс абстрактных фруктов, который бы разделял общую ответственность и общее поведение.

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

С точки зрения кодирования:

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

  • Другое отличие состоит в том, что объект может реализовать столько интерфейсов, сколько вам нужно, но у вас может быть только один абстрактный класс из-за «проблемы с алмазами» (ознакомьтесь здесь, чтобы узнать почему! http://en.wikipedia.org/wiki/ Multiple_inheritance # The_diamond_problem )

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

PS: «это» / «должен делать», вызванный ответом Вивека Вермани, я не хотел красть его ответ, просто чтобы повторно использовать термины, потому что я им нравился!

Кроме того, просто хотелось бы добавить здесь, что только потому, что любой другой язык OO имеет какие-то интерфейсы и абстракцию тоже не означает, что они имеют то же значение и цель, что и в PHP. Использование абстракции / интерфейсов несколько отличается, в то время как интерфейсы в PHP фактически не имеют реальной функции. Они просто используются по смысловым и связанным с схемами причинам. Суть заключается в том, чтобы проект был максимально гибким, расширяемым и безопасным для будущих расширений независимо от того, имеет ли разработчик более поздний план использования или нет.

Если ваш английский не является родным, вы можете найти, что такое Абстракция и Интерфейсы. И ищите синонимы.

И это может помочь вам в качестве метафоры:

ИНТЕРФЕЙС

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

Дело здесь

– сделать это правильно
– быть осторожным
– чтобы предотвратить то, что может испортиться (например, слишком много клубники или что-то еще)
– чтобы облегчить людям, которые это пробовали
– рассказать вам, как долго это делать (например, стильный)
– рассказать, какие вещи вы МОЖЕТЕ, но НЕ

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

АБСТРАКЦИИ

Чтобы продолжить эту метафору здесь … представьте, вы квест на этот раз, поедая этот торт. Затем вы пытаетесь использовать этот торт, используя рецепт. Но вы хотите добавить новые ингредиенты или изменить / пропустить шаги, описанные в рецепте. Итак, что будет дальше? Планируйте другую версию этого пирога. На этот раз с черными ягодами, а не соломенными ягодами и большим количеством ванильного крема … вкуснятина.

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

Теперь вы хотите обмениваться ингредиентами и шагами, и они ДОЛЖНЫ быть определены в новой версии этого торта. Это абстрактные методы, которые должны быть определены для нового пирога, потому что в торте должны быть фрукты, но что? Итак, на этот раз вы берете черные ягоды. Готово.

Там вы идете, вы расширили торт, следовали за интерфейсом и отвлеклись от него.

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

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

Давайте возьмем пример. Компьютерный монитор является сущностью и должен быть представлен как класс.

 class Monitor{ private int monitorNo; } 

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

 interface Display{ void display(); } 

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

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

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

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

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