В чем смысл интерфейсов в PHP?

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

Абстрактные классы позволяют вам делать то же самое, наряду с добавлением кода к методу.

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

Мне сказали, что это связано с теорией OO от С++ к Java, на которой основывается материал PHP OO. Является ли понятие полезным в Java, но не в PHP? Разве это просто способ удержания от заполнителей, заполненных абстрактным классом? Я что-то пропустил?

Ответ 1

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

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

Ответ 2

Концепция полезна всем в объектно-ориентированном программировании. Для меня я думаю о интерфейсе как о контракте. До тех пор, пока мой класс и ваш класс согласны с этим контрактом подписи этого метода, мы можем "взаимодействовать". Что касается абстрактных классов, то я вижу больше базовых классов, которые заглушают некоторые методы, и мне нужно заполнить детали.

Ответ 3

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

Одна из таких проблем:

"Алмазная проблема" (иногда называемая "смертельным алмазом смерти") - это неоднозначность, возникающая, когда два класса B и C наследуют от A, а класс D наследует от B и C. Если в есть метод, который B и C переопределили, и D не переопределяет его, тогда какую версию метода наследует D: версию B или версию C?

Источник: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

Почему/Когда использовать интерфейс? Пример... Все автомобили в мире имеют одинаковый интерфейс (методы)... AccelerationPedalIsOnTheRight(), BrakePedalISOnTheLeft(). Представьте, что у каждой автомобильной марки эти "методы" будут отличаться от другой марки. У БМВ были бы тормоза на правой стороне, и у Хонды были бы тормоза на левой стороне колеса. Люди должны были бы узнать, как работают эти "методы" каждый раз, когда покупали автомобиль другой марки. Вот почему хорошая идея иметь один и тот же интерфейс в нескольких "местах".

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

// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{   
    public function Create($personObject);
}

class MySqlPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Create a new person in MySql database.
    }
}

class MongoPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
    }
}

Таким образом, метод Create() всегда будет использоваться одинаково. Не имеет значения, используем ли мы класс MySqlPerson или класс MongoPerson. То, как мы используем метод, остается прежним (интерфейс остается прежним).

Например, он будет использоваться следующим образом (везде в нашем коде):

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

Таким образом, что-то подобное не может произойти:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

Намного проще запомнить один интерфейс и использовать его везде, чем несколько разных.

Таким образом, внутренняя часть метода Create() может быть разной для разных классов, не затрагивая "внешний" код, который вызывает этот метод. $personObject код должен знать только то, что метод Create() имеет 1 параметр ($personObject), потому что внешний код будет использовать/вызывать метод. Внешний код не заботится о том, что происходит внутри метода; это только должно знать, как использовать/назвать это.

Вы можете сделать это и без интерфейса, но если вы используете интерфейс, он "безопаснее" (потому что он предотвращает ошибки). Интерфейс гарантирует, что метод Create() будет иметь одинаковую сигнатуру (одинаковые типы и одинаковое количество параметров) во всех классах, которые реализуют интерфейс. Таким образом, вы можете быть уверены, что ЛЮБОЙ класс, который реализует интерфейс IPersonService, будет иметь метод Create() (в этом примере) и будет нуждаться только в 1 параметре ($personObject) для $personObject/использования.

Класс, который реализует интерфейс, должен реализовать все методы, которые интерфейс делает/имеет.

Я надеюсь, что я не повторял себя слишком много.

Ответ 4

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

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

Например, скажем, у вас есть абстрактная учетная запись класса, из которой расширяются многие другие классы (типы учетных записей и т.д.). Он имеет определенный набор методов, которые применимы только к этой группе типов. Тем не менее, некоторые из этих подклассов учетной записи реализуют Versionable, Listable или Editable, поэтому их можно бросить в контроллеры, которые ожидают использования этих API. Контроллеру не важно, какой тип объекта он

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

Таким образом, я говорю, что подкласс FooUser НЕ является учетной записью, но они действуют как редактируемый объект. Аналогично, BarAccount распространяется из учетной записи, но не является подклассом User, но реализует Editable, Listable и также Versionable.

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

Ответ 5

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

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

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

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

Ответ 6

Вы будете использовать интерфейсы в PHP:

  • Чтобы скрыть реализацию - установите протокол доступа к классу объектов, измените базовую реализацию без рефакторинга во всех местах, где вы использовали объекты
  • Чтобы проверить тип - как при проверке того, что параметр имеет определенный тип $object instanceof MyInterface
  • Чтобы обеспечить проверку параметров во время выполнения
  • Чтобы реализовать несколько действий в одном классе (создавать сложные типы)

    класс Car реализует EngineInterface, BodyInterface, SteeringInterface {

так что объект Car ca теперь start(), stop() (EngineInterface) или goRight(), goLeft() (Рулевой интерфейс)

и другие вещи, о которых я не могу сейчас думать

Число 4 это, вероятно, самый очевидный вариант использования, с которым вы не можете обращаться с абстрактными классами.

От мышления в Java:

В интерфейсе говорится: "Это будет выглядеть все классы, реализующие этот конкретный интерфейс". Таким образом, любой код, который использует определенный интерфейс, знает, какие методы могут быть вызваны для этого интерфейса, и все. Таким образом, интерфейс используется для установления "протокола" между классами.

Ответ 7

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

Ниже приведен пример использования интерфейса, в котором абстрактный класс не подходит:
Допустим, у меня есть приложение календаря, которое позволяет пользователям импортировать данные календаря из внешних источников. Я бы написал классы для обработки импорта каждого типа источника данных (ical, rss, atom, json). Каждый из этих классов мог бы реализовать общий интерфейс, который обеспечивал бы, чтобы все они имели общие общедоступные методы, которые мое приложение должно получать.

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Затем, когда пользователь добавляет новый канал, я могу определить тип фида и использовать класс, разработанный для этого типа, для импорта данных. Каждый класс, написанный для импорта данных для определенного фида, имел бы совершенно другой код, в противном случае между классами может быть очень мало сходства, кроме того, что им требуется реализовать интерфейс, который позволяет моему приложению их потреблять. Если бы я использовал абстрактный класс, я бы очень легко проигнорировал тот факт, что я не переопределил метод getEvents(), который бы тогда разорвал мое приложение в этом экземпляре, тогда как использование интерфейса не позволяло моему приложению запускаться, если ЛЮБОЙ из методов определенные в интерфейсе, не существуют в классе, который его реализовал. Моему приложению не нужно заботиться о том, какой класс он использует для получения данных из фида, только в том, что методы, необходимые для получения этих данных, присутствуют.

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

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

Это выходит за рамки вопроса, но поскольку я использовал приведенный выше пример: Интерфейсы поставляются со своим набором проблем, если они используются таким образом. Мне нужно обеспечить вывод, который возвращается из методов, реализованных в соответствии с интерфейсом, и для этого я использую среду IDE, которая считывает блоки PHPDoc и добавляет возвращаемый тип в виде подсказки типа в блоке PHPDoc интерфейса, который затем перевести на конкретный класс, который его реализует. Мои классы, которые потребляют данные, выводимые из классов, реализующих этот интерфейс, будут, по крайней мере, знать, что ожидают массив, возвращенный в этом примере:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

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

Ответ 8

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

interface Readable {
  String read();
}

List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
  System.out.println(reader.read());

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

Динамически типизированные языки имеют понятие "утиная печать", где вам не нужны интерфейсы; вы можете предположить, что у объекта есть метод, который вы вызываете на него. Это работает вокруг проблемы в статически типизированных языках, где ваш объект имеет некоторый метод (в моем примере, read()), но не реализует интерфейс.

Ответ 9

По-моему, интерфейсы должны быть предпочтительнее не функциональных абстрактных классов. Я бы не удивился, если бы там было даже хитовую производительность, так как только один объект был создан, вместо того, чтобы разбирать два, объединив их (хотя, я не могу быть уверен, я не знаком с внутренней работой OOP PHP).

Верно, что интерфейсы менее полезны/значимы, чем по сравнению с, скажем, Java. С другой стороны, PHP6 представит еще больше намеков типа, включая подсказки типа для возвращаемых значений. Это должно добавить некоторое значение для интерфейсов PHP.

tl; dr: interfaces определяет список методов, которым необходимо следовать (think API), в то время как абстрактный класс дает некоторые базовые/общие функции, которые подклассы усовершенствуют для конкретных потребностей.

Ответ 10

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

В PHP вы можете применять несколько интерфейсов, разделяя их запятой (я думаю, я не считаю это чистым soloution).

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

Ответ 11

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

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

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

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

Ответ 12

Интерфейсы как твои гены.

Абстрактные классы как твои настоящие родители.

Их цели наследственные, но в случае абстрактных классов и интерфейсов то, что наследуется, более конкретно.

Ответ 13

Ниже приведены точки для интерфейса PHP

  • Он используется для определения требуемого отсутствия методов в классе [если вы хотите загрузить html, тогда требуется идентификатор и имя, поэтому в этом случае интерфейс включает setID и setName].
  • Интерфейс строго принуждает класс включать все методы, определяющие в нем.
  • Вы можете определять метод только в интерфейсе с общедоступной доступностью.
  • Вы также можете расширить интерфейс, например, класс. Вы можете расширить интерфейс в php, используя ключевое слово extends.
  • Расширение нескольких интерфейсов.
  • Вы не можете реализовать 2 интерфейса, если обе функции совместного доступа с тем же именем. Это приведет к ошибке.

Пример кода:

interface test{
    public function A($i);
    public function B($j = 20);
}

class xyz implements test{
    public function A($a){
        echo "CLASS A Value is ".$a;
    }
    public function B($b){
        echo "CLASS B Value is ".$b;
    }
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);

Ответ 14

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

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

2.Все методы в интерфейсе должны находиться в публичной области видимости.

3. Класс может реализовывать более одного интерфейса, в то время как он может наследовать только от одного абстрактного класса.

                                  interface                      abstract class
the code                     - abstract methods               - abstract methods
                             - constants                      - constants                  
                                                              - concrete methods
                                                              - concrete variables

access modifiers             
                             - public                         - public
                                                              - protected
                                                              - private
                                                                etc.
number of parents          The same class can implement
                           more than 1 interface              The child class can 
                                                              inherit only from 1 abstract class

Надеюсь, что это поможет любому понять!

Ответ 15

Я не знаю о других языках, какова концепция интерфейса там. Но для PHP я постараюсь объяснить это. Просто наберитесь терпения и, пожалуйста, прокомментируйте, если это помогло.

Интерфейс работает как "контракты", определяя, что делает набор подклассов, но не как они это делают.

Правило

  1. Интерфейс не может быть создан.

  2. Вы не можете реализовать какой-либо метод в интерфейсе, то есть он содержит только сигнатуру метода, но не детали (тело).

  3. Интерфейсы могут содержать методы и/или константы, но без атрибутов. Интерфейсные константы имеют те же ограничения, что и константы класса. Методы интерфейса неявно абстрактны.

  4. Интерфейсы не должны объявлять конструкторы или деструкторы, так как это детали реализации на уровне класса.
  5. Все методы в интерфейсе должны иметь публичную видимость.

Теперь давайте возьмем пример. Предположим, у нас есть две игрушки: одна - собака, а другая - кошка.

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

Когда пользователь нажимает кнопку "говорить", игрушка должна говорить, не важно, собака это или кошка.

Это хороший случай, чтобы использовать интерфейс, а не абстрактный класс, потому что реализации разные. Зачем? Помните

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

Ответ 16

Для меня это бесполезно, проверьте код ниже:

interface human{

    public function eat($food);

}

class activities implements human{

    public function eat($food){

        echo "I eat {$food}";

    }

    public function misc($misc){

        echo "I do {$misc}";

    }
}

function feed_1a(human $human, $food="nothing"){

    $human->eat($food);

}

function feed_1b(human $human, $misc="nothing"){

    $human->misc($misc);

}

$act = new activities();

echo feed_1a($act, "rice");

echo feed_1b($act, "coding");

function feed_2a($act, $food="nothing"){

    $act->eat($food);

}

function feed_2b($act, $misc="nothing"){

    $act->misc($misc);

}

$a = new activities();

echo feed_2a($a, "chicken");

echo feed_2b($act, "coding");