Интерфейс или абстрактный класс: какой использовать? - PullRequest
301 голосов
/ 29 ноября 2009

Пожалуйста, объясните, когда я должен использовать PHP interface, и когда я должен использовать abstract class?

Как я могу изменить abstract class на interface?

Ответы [ 11 ]

431 голосов
/ 29 ноября 2009

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

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

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

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

152 голосов
/ 05 мая 2011

Различия между Abstract Class и Interface:

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

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

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

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

Интерфейс

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

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

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

117 голосов
/ 31 января 2013

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

<?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 и метода 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 
62 голосов
/ 29 ноября 2009

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

37 голосов
/ 18 февраля 2013

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

Например:

<?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)
13 голосов
/ 29 ноября 2009

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

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

12 голосов
/ 17 мая 2017

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

Если ваш английский не является родным, вы можете посмотреть, что такое Abstraction and Interfaces. И ищите синонимы тоже.

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

ИНТЕРФЕЙС

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

Дело здесь

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

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


АБСТРАКЦИЯ

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

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

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

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

10 голосов
/ 24 мая 2016

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

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

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

  • Интерфейсы могут помочь управлять тем фактом, что, как и 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();
}
10 голосов
/ 10 марта 2014

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

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

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

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

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

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

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

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

6 голосов
/ 12 октября 2015

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

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

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

class Monitor{
    private int monitorNo;
}

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

interface Display{
    void display();
}

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

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