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

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

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

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

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

Solutions Collecting From Web of "В чем смысл интерфейсов в PHP?"

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

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

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

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

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

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

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

Почему / когда использовать интерфейс? Пример … Все автомобили в мире имеют один и тот же интерфейс (методы) … AccelerationPedalIsOnTheRight() , BrakePedalISOnTheLeft() . Представьте себе, что каждая марка автомобиля будет иметь эти «методы», отличные от другого бренда. У BMW были бы тормоза с правой стороны, а у Honda были бы тормоза на левой стороне колеса. Люди должны были бы узнать, как эти «методы» работают каждый раз, когда они покупают другой автомобиль. Вот почему неплохо иметь один и тот же интерфейс в нескольких местах.

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

 // 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() может быть разным для разных классов, не затрагивая «внешний» код, который вызывает этот метод. Весь внешний код должен знать, что метод Create() имеет 1 параметр ( $personObject ), так как внешний код будет использовать / вызывать метод. Внешний код не волнует, что происходит внутри метода; он должен знать только, как использовать / называть его.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. Чтобы скрыть реализацию – установите протокол доступа к классу объектов, измените базовую реализацию без рефакторинга во всех местах, где вы использовали объекты
  2. Проверить тип – как при проверке того, что параметр имеет определенный тип $object instanceof MyInterface
  3. Обеспечить проверку параметров во время выполнения
  4. Чтобы реализовать множественное поведение в одном классе (строить сложные типы)

класс Car реализует EngineInterface, BodyInterface, SteeringInterface { так что объект Car теперь start() , stop() (EngineInterface) или goRight() , goLeft() (интерфейс рулевого управления)

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

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

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

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

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

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

 <?php interface ImportableFeed { public function getEvents(); } 

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

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

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

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

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

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

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

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

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

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

 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 ()), но не реализует интерфейс.

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

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

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

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

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

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

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

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

  1. Он используется для определения требуемого отсутствия методов в классе [если вы хотите загрузить html, тогда требуется идентификатор и имя, поэтому в этом случае интерфейс включает setID и setName].
  2. Интерфейс строго принуждает класс включать в него все методы.
  3. Вы можете определять только метод в интерфейсе с общедоступной доступностью.
  4. Вы также можете расширить интерфейс, например, класс. Вы можете расширить интерфейс в php, используя ключевое слово extends.
  5. Расширьте несколько интерфейсов.
  6. Вы не можете реализовать 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);