Інтерфейс чи абстрактний клас: який використовувати?


327

Будь ласка, поясніть, коли я повинен використовувати PHP interfaceі коли я повинен використовувати abstract class?

Як я можу змінити свій abstract classзапис на interface?

Відповіді:


458

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

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

Слід пам’ятати також, що клієнтські класи можуть поширювати лише один абстрактний клас, тоді як вони можуть реалізовувати декілька інтерфейсів. Отже, якщо ви визначаєте свої контракти на поведінку в абстрактних класах, це означає, що кожен дочірній клас може відповідати лише одному договору. Іноді це добре, коли ти хочеш примусити своїх користувачів-програмістів до певного шляху. В інших випадках це було б погано. Уявіть, якби інтерфейси Лічильника та Ітератора PHP були абстрактними класами замість інтерфейсів.

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


12
Я цілий день намагався зрозуміти звичаї abstractта interfaceзаняття, у вашому дописі все було зрозуміло. Велике спасибі Алану
afarazit

4
Ще одна перевага абстрактних класів - це можливість визначати абстрактні захищені методи . Не завжди корисно, але може стати в нагоді в деяких архітектурах.
netcoder

Тому в багатьох випадках ми повинні використовувати абстрактний клас через гнучкість - ось мій висновок :)
ymakux

3
@volocuga: необов'язково, як зазначав Алан, можна розширити лише один конспект. Мені особисто не подобається, що конспект реалізує ідею інтерфейсу, оскільки це сприяє обфускуванню коду і менш пряме, IMO.
Префікс

171

Відмінності між Abstract Classі та Interface:

Анотація заняття

Абстрактний клас може надати деяку функціональність і залишити решту для похідного класу .

  • Похідний клас може або не може змінювати конкретні функції, визначені в базовому класі.

  • Дочірній клас, подовжений від абстрактного класу, повинен логічно бути пов'язаним.

Інтерфейс

Інтерфейс не може містити жодної функціональності . Він містить лише визначення методів.

  • Похідний клас ПОВИНЕН надати код для всіх методів, визначених в інтерфейсі .

  • Цілком різні та не пов'язані між собою класи можуть бути логічно згруповані разом за допомогою інтерфейсу.


1
Чи можете ви навести приклад із реального життя, щоб продемонструвати це?
RN Kushwaha

1
Яка різниця між abstract class X implements Yі class X implements Y?
Вебінан

3
@Webinan В abstract class X implements Yви заявляєте, що об'ємна функціональність X повинна бути реалізована у похідному класі, і те, що і абстрактний, і похідний клас повинні містити функції, визначені Y, тоді як class X implements Yлише мається на увазі, що клас X повинен містити функції, визначені в Y. Якщо ваш інтерфейс Y не призначений для реалізації будь-якого іншого класу, ніж XI, фактично пропустив би визначення Y як інтерфейсу і реалізував функції лише в Y як публічну / захищену / приватну абстрактну функцію, щоб переконатися, що вони реалізовані у похідному класі.
Calle Bergström

1
Інтерфейси можуть не тільки містити визначення методів, вони також можуть містити константи
Thielicious

Сподобалось ваше порівняння. Тож хотілося щось додати. Інтерфейси можуть мати константи класу поза полем, тоді як абстрактний клас не може.
Номан Ібрагім

128

Навіщо використовувати абстрактні класи? Далі наведено простий приклад. Скажімо, у нас є такий код:

<?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();

Що це за смак? Що ж, це не має великого сенсу, тому ви не повинні цього робити. Це досягається, зробивши клас фруктів абстрактним, а також методом їжі всередині нього.

<?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 

2
Привіт, ця відповідь, ймовірно, отримала зворотний зв'язок через те, як вона форматована. Було б добре, якби це був не великий блок коду (чотири пробіли перетворюють щось у блок коду, скасовують текст, щоб вийняти його з блоку), і якби це було скопійовано звідкись (це виглядає так) було б ввічливо кредитувати їх.
Каміло Мартін

9
Я люблю тебе за фруктовий приклад людина! З того моменту, як я почав вивчати php, цей приклад дає мені зрозуміти спасибі багато
Рахіль,

23
+1 What does that taste like? Well, it doesn't make much sense, so you shouldn't be able to do that.Тепер я знаю абстрактне!
Вебінан

Що робить finalключове слово? Чудовий пост, дякую.
Gus

1
@VineeshKalarickal Те, що я не розумію на прикладі Person, - це різниця між: 1) використанням абстрактного класу Person (як у прикладі); 2) написання Особи як стандартного класу і змушення працівника та студента перекрити метод write_info ().
Ferex

66

Найкращою практикою є використання інтерфейсу для визначення контракту та абстрактного класу як лише однієї його реалізації. Цей абстрактний клас може заповнити багато шаблону, щоб ви могли створити реалізацію, просто перекресливши те, що вам потрібно або хочете, не змушуючи вас використовувати певну реалізацію.


37

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

Наприклад:

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

Таким чином, кожен, хто читає мій код (і хто знає, що таке Шаблон декоратора), відразу дізнається а) як я будую свій парсер і б) зможе побачити, які методи використовуються для реалізації схеми декоратора.

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

Пізно, і я не можу придумати кращого прикладу псудо-коду, але ось що:

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

1
Який дивовижний приклад! ;)
Джоел Мерфі

@ matt2000 Не є сексистським І працює також для одностатевих шлюбів. Чудова редакція. :)
Остін Хуген

4
Це приголомшливий приклад! Однак я не думаю, що ви можете створити абстрактний клас, а навпаки, клас, який походить від абстрактного класу
Аріель Нгуен

2
не вбивав би, щоб зробити sudo-код хоча б схожим на мову, про яку йде мова. хтось повинен покласти туди кілька доларів.
Я боровся ведмедя один раз.

2
Хоча приклад смішний, ми не можемо створити абстрактний клас безпосередньо, будь ласка, внесіть зміни в приклад, щоб люди не могли вважати його дійсним використанням у PHP.
saji89

16

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

Інтерфейс - це договір поведінки без будь-якої реалізації.


16

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

Якщо ваша англійська мова не є рідною, ви можете дізнатися, що таке абстракція та інтерфейси. І шукайте синоніми теж.

І це може допомогти вам як метафора:

ІНТЕРФЕЙС

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

Справа тут в тому

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

Саме ЦЕ ТО описує інтерфейси. Це посібник, набір інструкцій, який дотримується змісту рецепту. Так само, як якщо б ви створили проект у PHP і хочете надати код на GitHub або зі своїми товаришами чи ще. Інтерфейс - це те, що люди можуть робити, а що не слід. Правила, які його дотримуються - якщо ви не дотримуєтесь цього, вся конструкція буде порушена.


РЕЗУЛЬТАТ

Щоб продовжити цю метафору тут ... уявіть, ви цього разу є гостем, який їв цей торт. Тоді ви пробуєте цей торт, використовуючи рецепт зараз. Але ви хочете додати нові інгредієнти або змінити / пропустити етапи, описані в рецепті. То що далі? Плануйте іншу версію цього торта. Цього разу з чорними ягодами, а не з солом'яними ягодами та ще ванільним кремом ... смачним.

Це те, що ви могли б розглянути як продовження оригінального торта. Ви в основному робите абстракцію, створюючи новий рецепт, тому що це ліл інший. У ньому є кілька нових кроків та інших інгредієнтів. Однак версія чорної ягоди має деякі частини, які ви взяли на себе з оригіналу - це основні етапи, які повинен мати кожен вид цього торта. Як і інгредієнти, так само як молоко. Саме таким є кожен похідний клас.

Тепер ви хочете обмінятись інгредієнтами та кроками, і ЦЕ ОБОВ'ЯЗКОВО визначити у новій версії торта. Це абстрактні методи, які слід визначити для нового торта, адже в торті має бути плід, але який? Тож ви цього разу берете чорні ягоди. Зроблено.

Ось, ви продовжили торт, дотримувались інтерфейсу та абстрагували його кроки та інгредієнти.


1
Це було моїм улюбленим порівнянням всього, що стосується PHP. Це справді мало сенс. Дякую!
cbloss793

13

Щоб додати до деяких вже чудових відповідей:

  • Абстрактні класи дозволяють надати певну ступінь реалізації, інтерфейси - це чисті шаблони. Інтерфейс може визначати лише функціональність , він ніколи не може його реалізувати.

  • Будь-який клас, який реалізує інтерфейс, зобов'язується реалізувати всі визначені ним методи або повинен бути оголошений абстрактним.

  • Інтерфейси можуть допомогти керувати тим фактом, що, як і Java, PHP не підтримує багаторазове успадкування. Клас PHP може поширювати лише одного батька. Однак ви можете пообіцяти класу реалізувати стільки інтерфейсів, скільки вам потрібно.

  • тип: для кожного інтерфейсу, який він реалізує, клас приймає відповідний тип. Оскільки будь-який клас може реалізувати інтерфейс (або більше інтерфейсів), інтерфейси ефективно з'єднують типи, які в іншому випадку не пов'язані.

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

    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();
}

12

З філософської точки зору:

  • Абстрактний клас представляє відносини "є". Скажімо, у мене є фрукти, я б мав абстрактний клас фруктів, який розділяє загальну відповідальність та спільну поведінку.

  • Інтерфейс представляє "слід робити". На мій погляд, інтерфейс (на думку молодшого розробника) повинен бути названий дією чи чимось близьким до дії (Вибачте, не можу знайти слово, я не є носієм англійської мови) скажемо IEatable. Ви знаєте, що його можна їсти, але ви не знаєте, що їсте.

З точки зору кодування:

  • Якщо у ваших об’єктів дублюється код, це свідчить про те, що вони мають загальну поведінку, а це означає, що вам може знадобитися абстрактний клас для повторного використання коду, що ви не можете зробити з інтерфейсом.

  • Ще одна відмінність полягає в тому, що об’єкт може реалізувати стільки інтерфейсів, скільки вам потрібно, але у вас може бути лише один абстрактний клас через "алмазну проблему" (див. Тут, щоб знати, чому! Http://en.wikipedia.org/wiki/ Багаторазова спадщина # The_diamond_problem )

Я, мабуть, забуваю деякі моменти, але сподіваюся, що це може прояснити речі.

PS: "є" / "повинен робити" принесе відповідь Вівека Вермані, я не мав намір вкрасти його відповідь, а просто повторно використовувати умови, тому що мені вони сподобалися!


2
Я вважаю, що слово, яке ви шукаєте, їстівне.
Тревіс Вестон

1
Власне, я вважаю, що це "Дієслово", "те, що робить слово"
Grizly

7

Технічні відмінності між абстрактним класом та інтерфейсом уже вказані в інших відповідях точно. Я хочу додати пояснення, щоб вибрати між класом та інтерфейсом під час написання коду заради об’єктно-орієнтованого програмування.

Клас повинен представляти сутність, тоді як інтерфейс повинен представляти поведінку.

Візьмемо приклад. Монітор комп’ютера є сутністю і повинен бути представлений як клас.

class Monitor{
    private int monitorNo;
}

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

interface Display{
    void display();
}

Є багато інших речей, які слід розглянути, як пояснено в інших відповідях, але це найголовніше, що більшість людей ігнорує при кодуванні.


2
PHP не визначає типи повернення і ОП позначила це запитанняPHP
Purefan

1

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

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

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.