Интерфейсы объектов
Интерфейсы объектов позволяют создавать код, который указывает, какие методы должен реализовать класс, без необходимости описывания их функционала.
Интерфейсы объявляются так же, как и обычные классы, но с использованием ключевого слова interface. Тела методов интерфейсов должны быть пустыми.
Все методы, определенные в интерфейсы должны быть публичными, что следует из самой природы интерфейса.
implements
Для реализации интерфейса используется оператор implements. Класс должен реализовать все методы, описанные в интерфейсе; иначе произойдет фатальная ошибка. При желании классы могут реализовывать более одного интерфейса за раз, реализуемые интерфейсы должны разделяться запятой.
Замечание:
Класс не может реализовать два интерфейса, содержащих одноименную функцию, так как это повлечет за собой неоднозначность.
Замечание:
Интерфейсы могут быть унаследованы друг от друга, так же как и классы, с помощью оператора extends.
Замечание:
Сигнатуры методов в классе, реализующем интерфейс, должны точно совпадать с сигнатурами, используемыми в интерфейсе, в противном случае будет вызвана фатальная ошибка.
Константы (Constants)
Интерфейсы могут содержать константы. Константы интерфейсов работают точно так же, как и константы классов, за исключением того, что они не могут быть перекрыты наследующим классом или интерфейсом.
Примеры
Пример #1 Пример интерфейса
// Объявим интерфейс ‘iTemplate’
interface iTemplate
<
public function setVariable ( $name , $var );
public function getHtml ( $template );
>
// Реализуем интерфейс
// Это сработает нормально
class Template implements iTemplate
<
private $vars = array();
public function setVariable ( $name , $var )
<
$this -> vars [ $name ] = $var ;
>
public function getHtml ( $template )
<
foreach( $this -> vars as $name => $value ) <
$template = str_replace ( ‘<' . $name . '>‘ , $value , $template );
>
// Это не будет работать
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (iTemplate::getHtml)
// (Фатальная ошибка: Класс BadTemplate содержит 1 абстрактный метод
// и поэтому должнен быть объявлен абстрактным (iTemplate::getHtml))
class BadTemplate implements iTemplate
<
private $vars = array();
public function setVariable ( $name , $var )
<
$this -> vars [ $name ] = $var ;
>
>
?>
Пример #2 Расширяемые интерфейсы
<?php
interface a
<
public function foo ();
>
interface b extends a
<
public function baz ( Baz $baz );
>
// Это сработает
class c implements b
<
public function foo ()
<
>
public function baz ( Baz $baz )
<
>
>
// Это не сработает и выдаст фатальную ошибку
class d implements b
<
public function foo ()
<
>
Пример #3 Множественное наследование интерфейсов
<?php
interface a
<
public function foo ();
>
interface b
<
public function bar ();
>
interface c extends a , b
<
public function baz ();
>
class d implements c
<
public function foo ()
<
>
public function bar ()
<
>
Пример #4 Интерфейсы с константами
<?php
interface a
<
const b = ‘Константа интерфейса’ ;
>
// Выведет: Константа интерфейса
echo a :: b ;
// Вот это, однако, не будет работать, так как
// константы перекрывать нельзя.
class b implements a
<
const b = ‘Class constant’ ;
>
?>
Интерфейс, совместно с контролем типов, предоставляет отличный способ проверки того, что определенный объект содержит определенный набор методов. Смотрите также оператор instanceof и контроль типов.
Интерфейс PHP
В PHP ООП интерфейс — это класс, в котором все методы являются абстрактными и открытыми.
Быстро пробежимся по трем терминам, используемым в этом определении. Класс, абстрактный и открытый. Класс представляет собой шаблон или образец объекта, он является основой объектно-ориентированного программирования. Абстрактный — это специальный метод, который объявляется, но не имеет тела, только пустые скобки. Открытый — это модификатор доступа public , он связан с доступом к методам. Это означает, что он доступен из любого места: из этого класса, вне его пределов и из любого производного класса.
Интерфейс PHP представляет собой инструмент для создания надежного кода, определяющего методы, которые должны быть реализованы с помощью класса без определения обработки этих методов. Это мощная и простая концепция, используемая в объектно-ориентированном PHP . Для его создания используется ключевое слово interface , он определяется так же, как класс PHP . Методы, содержащиеся в нем, не имеют никакого функционала, но интерфейс задает, какие методы необходимы для других классов. Любой класс, наследующийся от интерфейса, должен содержать те же методы, которые содержатся в интерфейсе, иначе выдается ошибка.
В интерфейсе ООП PHP мы задаем только имена методов и их параметры, а реализованы они могут быть позже. Принято объявлять все методы интерфейса как public . Для реализации интерфейса используется ключевое слово implements . При необходимости класс может реализовать более одного интерфейса, между собой они разделяются запятой. Как и класс, интерфейс может содержать константы. Единственное отличие заключается в том, что они не могут быть переопределены в производном классе.
Определение и использование интерфейсов
Интерфейс определяется с помощью ключевого слова interface . Все методы в нем должны быть открытыми и абстрактными:
Это пустой интерфейс. Мы добавим в него абстрактный метод motion . Таким образом, мы создадим простой PHP 5 ООП интерфейс, который может быть реализован позже:
Этот интерфейс может быть реализован любым классом animal , все классы animal будут содержать метод motion . Давайте рассмотрим пример. Для реализации интерфейса мы используем ключевое слово implements :
PHP-интерфейс и абстрактные классы
Интерфейсы и абстрактные классы очень похожи по своей концепции. Они помогают в создании шаблонов для классов, которые будут использоваться в приложении. Согласно основам ООП PHP , они отличаются друг от друга следующим:
- В интерфейсе все методы являются абстрактными ( без реализации ). В абстрактном классе лишь некоторые методы являются абстрактными. Абстрактный класс должен содержать, по крайней мере, один абстрактный метод. Иначе это будет стандартный класс PHP ;
- В интерфейсе PHP все объявленные методы являются открытыми, а в абстрактном классе методы могут быть открытыми, частными или защищенными. Следовательно, для интерфейсов существует ограничение на использование модификаторов доступа, а в абстрактном классе таких ограничений нет;
- Класс может реализовать неограниченное количество интерфейсов. В то же время класс PHP может породить только один абстрактный класс;
- Вы должны переопределять все методы интерфейса, а в абстрактном классе у вас есть выбор: переопределять методы или оставить их без изменений;
- Интерфейсы имеют более строгие правила, чем абстрактные классы. Интерфейс PHP призван обеспечить определенную логику, он выступает в качестве пустой оболочки или шаблона для других классов.
Для чего используются интерфейсы PHP
Интерфейс помогает программисту мыслить в правильном направлении. С точки зрения PHP ООП , класс представляет собой шаблон, а объект — это набор возможностей. Мы в большей степени связаны с функционалом, что он делает, а не как он это делает. Поэтому мы определяем основной интерфейс, который является общим, а затем он реализуется, что помогает мыслить в правильном направлении.
Корректировка кода в будущем
Разработка на объектно-ориентированном PHP позволяет корректировать код в будущем, не затрагивая при этом основной функционал. При написании кода в будущем эти характеристики всегда будут учитываться, а затем включаться дополнительные функции, но это не окажет никакого влияния на основные характеристики уже существующих объектов.
Лучшая структура программы
Объектно-ориентированный PHP был реализован для улучшения структуры создаваемых приложений. Добавление интерфейсов в PHP способствует тому, что кодирование становится аккуратным и чистым. Интерфейсы препятствуют написанию кода, который трудно поддерживать и изменять. С помощью интерфейсов мы можем легко поддерживать и модифицировать код, что повышает эффективность работы программистов.
Добавление всех важных функций
С помощью интерфейсов PHP мы можем создавать базовый функционал, который может быть определен для различных объектов по-разному, а затем добавить другие функции, если это потребуется.
Php что такое интерфейс
Интерфейсы объектов позволяют создавать код, который указывает, какие методы должен реализовать класс, без необходимости определять, как именно они должны быть реализованы. Интерфейсы разделяют пространство имён с классами и трейтами, поэтому они не могут называться одинаково.
Интерфейсы объявляются так же, как и обычные классы, но с использованием ключевого слова interface вместо class . Тела методов интерфейсов должны быть пустыми.
Все методы, определённые в интерфейсах, должны быть общедоступными, что следует из самой природы интерфейса.
На практике интерфейсы используются в двух взаимодополняющих случаях:
- Чтобы позволить разработчикам создавать объекты разных классов, которые могут использоваться взаимозаменяемо, поскольку они реализуют один и тот же интерфейс или интерфейсы. Типичный пример — несколько служб доступа к базе данных, несколько платёжных шлюзов или разных стратегий кеширования. Различные реализации могут быть заменены без каких-либо изменений в коде, который их использует.
- Чтобы разрешить функции или методу принимать и оперировать параметром, который соответствует интерфейсу, не заботясь о том, что ещё может делать объект или как он реализован. Эти интерфейсы часто называют Iterable , Cacheable , Renderable и так далее, чтобы описать их поведение.
Интерфейсы могут определять магические методы, требуя от реализующих классов реализации этих методов.
Замечание:
Хотя они поддерживаются, использование конструкторов в интерфейсах настоятельно не рекомендуется. Это значительно снижает гибкость объекта, реализующего интерфейс. Кроме того, к конструкторам не применяются правила наследования, что может привести к противоречивому и неожиданному поведению.
implements
Для реализации интерфейса используется оператор implements . Класс должен реализовать все методы, описанные в интерфейсе, иначе произойдёт фатальная ошибка. При желании классы могут реализовывать более одного интерфейса, разделяя каждый интерфейс запятой.
Класс, реализующий интерфейс, может использовать для своих параметров имя, отличное от имени интерфейса. Однако, начиная с PHP 8.0, в языке поддерживаются именованные аргументы, и вызывающий код может полагаться на имя параметра в интерфейсе. По этой причине настоятельно рекомендуется, чтобы разработчики использовали те же имена параметров, что и реализуемый интерфейс.
Замечание:
Интерфейсы могут быть унаследованы друг от друга, так же, как и классы, с помощью оператора extends.
Замечание:
Класс, реализующий интерфейс, должен объявить все методы в интерфейсе с совместимой сигнатурой. Класс может реализовывать несколько интерфейсов, которые объявляют метод с одинаковым именем. В этом случае реализация должна следовать правилам совместимости сигнатуры для всех интерфейсов. Таким образом, можно применять ковариантность и контравариантность.
Константы ( Constants )
Интерфейсы могут содержать константы. Константы интерфейсов работают точно так же, как и константы классов. До PHP 8.1.0 они не могли быть переопределены классом или интерфейсом, который их наследует.
Примеры
Пример #1 Пример интерфейса
// Объявим интерфейс ‘Template’
interface Template
<
public function setVariable ( $name , $var );
public function getHtml ( $template );
>
// Реализация интерфейса
// Это будет работать
class WorkingTemplate implements Template
<
private $vars = [];
public function setVariable ( $name , $var )
<
$this -> vars [ $name ] = $var ;
>
public function getHtml ( $template )
<
foreach( $this -> vars as $name => $value ) <
$template = str_replace ( ‘<' . $name . '>‘ , $value , $template );
>
// Это не будет работать
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (Template::getHtml)
// (Фатальная ошибка: Класс BadTemplate содержит 1 абстрактный метод
// и поэтому должен быть объявлен абстрактным (Template::getHtml))
class BadTemplate implements Template
<
private $vars = [];
public function setVariable ( $name , $var )
<
$this -> vars [ $name ] = $var ;
>
>
?>
Пример #2 Наследование интерфейсов
<?php
interface A
<
public function foo ();
>
interface B extends A
<
public function baz ( Baz $baz );
>
// Это сработает
class C implements B
<
public function foo ()
<
>
public function baz ( Baz $baz )
<
>
>
// Это не сработает и выдаст фатальную ошибку
class D implements B
<
public function foo ()
<
>
Пример #3 Совместимость с несколькими интерфейсами
<?php
class Foo <>
class Bar extends Foo <>
interface A <
public function myfunc ( Foo $arg ): Foo ;
>
interface B <
public function myfunc ( Bar $arg ): Bar ;
>
class MyClass implements A , B
<
public function myfunc ( Foo $arg ): Bar
<
return new Bar ();
>
>
?>
Пример #4 Множественное наследование интерфейсов
<?php
interface A
<
public function foo ();
>
interface B
<
public function bar ();
>
interface C extends A , B
<
public function baz ();
>
class D implements C
<
public function foo ()
<
>
public function bar ()
<
>
Пример #5 Интерфейсы с константами
<?php
interface A
<
const B = ‘Константа интерфейса’ ;
>
// Выведет: Константа интерфейса
echo A :: B ;
class B implements A
<
const B = ‘Константа класса’ ;
>
// Выведет: Константа класса
// До PHP 8.1.0 этот код не будет работать,
// потому что было нельзя переопределять константы.
echo B :: B ;
?>
Пример #6 Интерфейсы с абстрактными классами
<?php
interface A
<
public function foo ( string $s ): string ;
public function bar ( int $i ): int ;
>
// Абстрактный класс может реализовывать только часть интерфейса.
// Классы, расширяющие абстрактный класс, должны реализовать все остальные.
abstract class B implements A
<
public function foo ( string $s ): string
<
return $s . PHP_EOL ;
>
>
class C extends B
<
public function bar ( int $i ): int
<
return $i * 2 ;
>
>
?>
Пример #7 Одновременное расширение и внедрение
// Порядок ключевых слов здесь важен. «extends» должно быть первым.
class Two extends One implements Usable , Updatable
<
/* . */
>
?>
Интерфейс, совместно с объявлениями типов, предоставляет отличный способ проверки того, что определённый объект содержит определённый набор методов. Смотрите также оператор instanceof и объявление типов.
PHP: Используем Интерфейсы для улучшения кода
Эта статья предназначена для разработчиков, которые имеют базовые представления о концепциях ООП (объектно-ориентированного программирования) и использовании наследования в PHP. Если вы знаете, как использовать наследование в своём PHP коде, мы надеемся, что эта статья будет понятной.
Что такое Интерфейсы
В общих чертах, интерфейсы — описание того, что ваш класс должен делать. Их можно использовать для гарантии, что любой класс реализующий интерфейс, будет включать каждый публичный метод определённый в интерфейсе.
Интерфейсы должны:
- Использоваться для определения публичных методов класса.
- Использоваться для определения констант класса.
Интерфейсы не должны:
- Использоваться сами по себе.
- Использоваться для определения приватных или защищённых методов класса.
- Использоваться для определения свойств класса.
Интерфейсы используются для определения публичных методов, которые должен включать класс. Важно помнить, что определены только сигнатуры методов и что они не включают тело метода (как вы обычно видите в методе класса). Это связано с тем, что интерфейсы используются только для определения взаимодействия между объектами, а не для определения взаимодействия и поведения, как в классе. Что дать немного контекста, в этом примере показан пример интерфейса, который определяет несколько общедоступных методов:
Согласно php.net, интерфейсы служат нескольким целям:
- Позволить разработчикам создавать объекты разных классов, которые могут использоваться взаимозаменяемо, поскольку они реализуют один и тот же интерфейс или интерфейсы. Типичный пример — несколько служб доступа к базе данных, несколько платёжных шлюзов или разные стратегии кэширования. Различные реализации могут быть заменены, не требуя каких-либо изменений в коде, который их использует.
- Позволить функции или методу принимать и оперировать параметром, который соответствует интерфейсу, не заботясь о том, что ещё может делать объект или как он реализован. Эти интерфейсы часто называют Iterable , Cacheable , Renderable и т.д., чтобы описать поведение.
Использование Интерфейсов в PHP
Интерфейсы могут быть бесценной частью кодовых баз ООП (объектно-ориентированного программирования). Они позволяют нам отделить наш код и улучшить расширяемость. Для иллюстрации этого, давайте рассмотрим следующий класс:
Как вы видите, мы определили класс с методом возвращающим строку. Таким образом, мы определили поведение метода, мы можем увидеть как getName() формирует возвращаемую строку. Однако допустим, что мы вызываем этот метод в нашем коде внутри другого класса. Другой класс не заботит то, как была сформирована эта строка, его заботит только то, что бы она была возвращена. Например, давайте посмотрим, как мы могли бы вызвать этот метод в другом классе:
Хотя вышеприведённый код работает, давайте представим, что мы хотим добавить функциональность для загрузки пользовательского отчёта из класса UserReport . Конечно, мы не можем использовать существующий метод в нашем ReportDownloadService , потому что мы установили, что можно передавать только класс BlogReport . Итак, нам нужно переименовать существующий метод, а затем добавить новый метод, как показано ниже:
Хотя вы этого не видите, давайте предположим, что остальные методы в приведённом выше классе используют идентичный код для создания загрузки. Мы могли бы превратить их общий код в методы, но, вероятно, у нас всё равно будет какой-то общий код. Кроме того, у нас будет несколько точек входа в класс, выполняющий почти идентичный код. Это потенциально может привести к дополнительной работе в будущем при попытке расширить код или добавить тесты.
Представим, что мы создаём новый AnalyticsReport ; нам нужно добавить в класс новый метод downloadAnalyticsReportPDF() . Как вы видите этот файл может начать быстро расти. Это может быть идеальным местом для использования интерфейсов.
Давайте создадим один; назовём его DownloadableReport и определим так:
Теперь вы можете обновить BlogReport и UsersReport для реализации интерфейса DownloadableReport , как показано в примере ниже. Но обратите внимание, я намеренно неправильно написал код для UsersReport , что бы кое-что продемонстрировать!
Если бы мы попытались запустить наш код, мы бы получили ошибки по следующим причинам:
- Пропущен метод getHeaders() .
- У метода getName() не задан тип возвращаемого значения. Но в сигнатуре метода интерфейса тип определён.
- У метода getData() задан тип возвращаемого значения, он не соответствует определённому в сигнатуре метода интерфейса.
Итак, что бы обновить UsersReport , для правильной реализации интерфейса DownloadableReport , нужно заменить его на следующий код:
Теперь, когда оба класса отчётов реализуют один и тот же интерфейс, мы можем обновить ReportDownloadService следующим образом:
Теперь мы можем передать объект UsersReport или BlogReport в метод downloadReportPDF() без каких либо ошибок. Потому что мы знаем, что необходимые методы нужные для классов отчёта существуют и возвращают данные ожидаемого типа.
В результате передачи интерфейса методу, а не классу, позволило нам слабо связать ReportDownloadService и классы отчётов в зависимости от того, что делают методы, а не от того, как они это делают.
Если бы мы захотели создать новый AnalyticsReport , мы могли бы заставить его реализовать тот же интерфейс, и это позволило бы нам передать объект отчёта в тот же метод downloadReportPDF() без необходимости добавлять какие-либо новые методы. Это может быть полезно, если вы хотите создать свой пакет или фреймворк, и хотите дать разработчику возможность создавать свой собственный класс. Вы можете просто сказать им, какой интерфейс реализовать, и они смогут создать собственный класс. Например, в Laravel вы можете создать собственный класс драйвера кэша, реализовав интерфейс Illuminate\Contracts\Cache\Store .
Помимо использования интерфейсов для улучшения актуального кода, мне нравятся интерфейсы, потому что они действуют как концепция «Документация — это код». Например, если я пытаюсь выяснить что может класс, а что не может, я обычно сначала смотрю на интерфейс, а затем на класс, который его реализует. Он сообщает вам все методы, которые могут быть вызваны, и не нужно заботиться о том, что у них под капотом.
Как заметили многие мои читатели Laravel-разработчик, довольно часто термины «контракт» и «интерфейс» используются как взаимозаменяемые. Согласно документации Laravel, контракты Laravel — это наборы интерфейсов, которые определяют основные сервисы предоставляемые фреймворком . Итак, важно помнить, что контракт — это интерфейс, но интерфейс необязательно является контрактом. Обычно контракт — это просто интерфейс, предоставляемый фреймворком. Для получения информации об использовании контрактов я рекомендую почитать документацию поскольку в ней хорошо разбирается то как их использовать и когда их использовать.
Вывод
Надеюсь, что прочитав эту статью вы получили краткий обзор того, что такое интерфейсы, как их использовать в PHP, об их преимуществах и использовании.
Interfaces
A class can implement a set of capabilities — herein called a contract — through what is called an interface. An interface is a set of method declarations and constants. Note that the methods are only declared, not defined; that is, an interface defines a type consisting of abstract methods, where those methods are implemented by client classes as they see fit. An interface allows unrelated classes to implement the same facilities with the same names and types without requiring those classes to share a common base class.
An interface can extend one or more other interfaces, in which case, it inherits all members from its base interface(s).
Interface Declarations
Syntax
Constraints
An interface must not be derived directly or indirectly from itself.
Every qualified-name must name an interface type.
Semantics
An interface-declaration defines a contract that one or more classes can implement.
Interface names are case-insensitive.
The optional interface-base-clause specifies the base interfaces from which the interface being defined is derived. In such a case, the derived interface inherits all the members from the base interfaces.
Examples
Interface Members
Syntax
Semantics
The members of an interface are those specified by its interface-member-declaration, and the members inherited from its base interfaces.
An interface may contain the following members:
-
– the constant values associated with the interface. – placeholders for the computations and actions that can be performed by implementers of the interface.
Constants
Constraints
All constants declared in an interface must be implicitly or explicitly public.
Semantics
An interface constant is just like a class constant, except that an interface constant cannot be overridden by a class that implements it nor by an interface that extends it.
Examples
Methods
Constraints
All methods declared in an interface must be implicitly or explicitly public, and they must not be declared abstract .
Semantics
An interface method is just like an abstract method.
Examples
Predefined Interfaces
Interface ArrayAccess
This interface allows an instance of an implementing class to be accessed using array-like notation. This interface is defined, as follows:
The interface members are defined below:
Name | Purpose |
---|---|
offsetExists | This instance method returns TRUE if the instance contains an element with key $offset , otherwise, FALSE . |
offsetGet | This instance method gets the value having key $offset . It may return by value or byRef. (Ordinarily, this wouldn’t be allowed because a class implementing an interface needs to match the interface’s method signatures; however, the Engine gives special treatment to ArrayAccess and allows this). This method is called when an instance of a class that implements this interface is subscripted in a non-lvalue context. |
offsetSet | This instance method sets the value having key $offset to $value. It returns no value. This method is called when an instance of a class that implements this interface is subscripted in a modifiable-lvalue context. |
offsetUnset | This instance method unsets the value having key $offset . It returns no value. |
Interface Iterator
This interface allows instances of an implementing class to be treated as a collection. This interface is defined, as follows:
The interface members are defined below:
Name | Purpose |
---|---|
current | This instance method returns the element at the current position. |
key | This instance method returns the key of the current element. On failure, it returns NULL ; otherwise, it returns the scalar value of the key. |
next | This instance method moves the current position forward to the next element. It returns no value. From within a foreach statement, this method is called after each loop. |
rewind | This instance method resets the current position to the first element. It returns no value. From within a foreach statement, this method is called once, at the beginning. |
valid | This instance method checks if the current position is valid. It takes no arguments. It returns a bool value of TRUE to indicate the current position is valid; FALSE , otherwise. This method is called after each call to Iterator::rewind() and Iterator::next() . |
Interface IteratorAggregate
This interface allows the creation of an external iterator. This interface is defined, as follows:
The interface members are defined below:
Name | Purpose |
---|---|
getIterator | This instance method retrieves an iterator, which implements Iterator or Traversable . It throws an Exception on failure. |
Interface Throwable
This type is the base interface for the type of any object that can be thrown via a throw statement. A user-written class cannot implement Throwable directly. Instead, it must extend Error or Exception .
This type is defined, as follows:
The interface members are defined below:
Name | Purpose |
---|---|
__toString | string ; retrieves a string representation of the exception in some unspecified format |
getCode | int ; retrieves the exception code |
getFile | string ; retrieves the name of the script where the exception was generated |
getLine | int ; retrieves the source line number in the script where the exception was generated |
getMessage | string ; retrieves the exception message |
getPrevious | Throwable ; retrieves the previous exception, if one exists; otherwise returns NULL |
getTrace | array ; retrieves the function stack trace information as an array |
getTraceAsString | string ; retrieves the function stack trace information formatted as a single string in some unspecified format |
Interface Traversable
This interface is intended as the base interface for all traversable classes. This interface is defined, as follows:
This interface has no members.
Interface Serializable
This interface provides support for custom serialization. It is defined, as follows:
Интерфейсы (ключевое слово `interface`) — PHP: Введение в ООП
Вместе с классами в PHP широко используется языковая конструкция "интерфейс". В этом уроке мы рассмотрим техническую сторону вопроса, а потом поговорим о смысле. Про последнее я сейчас могу сказать немного, потому что полноценный разговор про суть интерфейсов у нас пойдёт во время изучения полиморфизма в последующих курсах. А пока достаточно иметь общее представление об интерфейсах, так как без них не получится окунуться во фреймворки.
Интерфейс в PHP — конструкция языка, описывающая абстрактный тип данных (АТД). Напомню, что АТД определяет набор операций (функций), независимых от конкретной реализации типа (в нашем случае класса) для манипулирования его значениями. На практике интерфейсы содержат определения функций (то есть описание их сигнатур) без их реализации.
Хотя данная конструкция для нас в новинку, само понятие интерфейса используется на протяжении всего курса. В первую очередь это рассуждения о типах. Для оперирования точками на плоскости нам не нужна "реализация" точек. Достаточно того, что мы представляем их визуально и знаем операции, выполняемые над ними. То же самое касается и более базовых концепций, например, чисел и любых арифметических операций. Задумывались ли вы над тем, как на самом деле выполняются арифметические операции? Ответ на этот вопрос гораздо сложнее, чем может показаться на первый взгляд, и он зависит не только от языка, но и от конкретного аппаратного обеспечения (железа). Однако незнание ответа не мешает нам пользоваться числами, строками и массивами, не зная их устройства.
То, что раньше мы описывали словами и держали в голове, теперь явно записано в виде кода. Декартова точка — это АТД с тремя операциями:
- Создание точки из двух значений
- Извлечение координаты X
- Извлечение координаты Y
По сути, прикладному коду больше ничего знать о точках и не нужно. Естественно, если нам понадобятся новые операции, то мы всегда можем их добавить, тем самым расширив интерфейс. Свои собственные АТД можно менять как угодно и когда угодно, только учтите, что изменение интерфейса обычно приводит к необходимости править код, использующий его.
Сама по себе конструкция Interface никак не влияет на остальной код. Недостаточно просто создать интерфейс, в этом нет смысла. Интерфейс должен быть реализован, и тогда он начнёт приносить пользу.
Реализация интерфейса происходит за счёт ключевого слова implements , за которым идёт название интерфейса. Интерпретатор проверяет, чтобы в классе были описаны все функции интерфейса и их сигнатуры совпадали, а если это не так, то возникает ошибка. Реализация интерфейса никак не ограничивает возможности по наполнению класса, другими словами, вы можете определять и добавлять в класс все, что хотите, помимо интерфейсных функций.
Насколько наличие интерфейсов критично для PHP программ? Например, можно в любой программе открыть все файлы с классами и удалить часть определения класса, которая описывает реализацию интерфейсов (слово implements и то, что идёт за ним). После этого не изменится ровным счётом ничего — программа продолжит выполняться так же, как и выполнялась. Но ситуация меняется, если использовать интерфейс в сигнатурах функций и методов вместо классов.
Во время выполнения программы PHP проверяет, реализует ли класс соответствующий интерфейс, и если нет, то возникает ошибка. Причём проверка идёт именно на наличие записи implements в определении класса, а не на факт того, что методы определены (проверка реализации интерфейса гарантирует это).
Такая запись позволяет коду завязываться не на конкретную реализацию точек, а на их интерфейс. Это — ключевая мысль, которую имеет смысл обсуждать подробнее вместе с полиморфизмом.
Отдельно стоит сказать, что один класс может реализовывать любое число интерфейсов, в таком случае они описываются через запятую:
Здесь класс Stack реализует сразу три интерфейса. Это значит, что внутри него должны быть реализованы методы всех указанных интерфейсов.
Если у разных интерфейсов окажутся методы с одинаковым названием, но разной сигнатурой, то возникнет ошибка. В таком случае придётся отказаться от одного из интерфейсов либо переделать их (например, переименовать методы)
Интерфейс Countable
В PHP встроен интерфейс Countable , а функция count умеет работать с любым объектом, реализующим этот интерфейс.
Php что такое интерфейс
Интерфейс определяет абстрактный дизайн, которому должен соответствовать применяющий его класс. Интерфейс определяет методы без реализации. А класс затем применяет интерфейс и реализует эти методы. Применение интерфейса гарантирует, что класс имеет определенный функционал, описываемый интерфейсом.
Интерфейс определяется с помощью ключевого слова interface , за которым следует имя интерфейса и блок кода интерфейса в фигурных скобках:
Здесь определен интерфейс Messenger . Внутри блока интерфейса в фигурных скобках определяются сигнатуры методов. Причем все эти методы могут быть только публичными, то есть с модификатором public , либо без модификатора доступа (что аналоично модификатору public):
Интерфейсы могут содержать лишь сигнатуры методов — наазвания функций и список параметров в скобках, после которого идет точка с запятой. Так, в данном случае объявлен метод send() . Он не имеет реализации — конкретную реализацию определит класс, который реализует этот интерфейс.
Для реализации классом интерфейса применяется ключевое слово implements , после которого указывается имя применяемого интерфейса:
В данном случае класс EmailMessenger реализует интерфейс Messenger. Если класс применяет интерфейс, то он должен реализовать все методы этого интерфейса. Так, в данном случае класс EmailMessenger определяет метод send() с некоторой реализацией.
Интерфейсы также могут наследоваться от других интерфейсов:
Когда нам могут понадобиться интерфейсы? Интерфейс — это контракт, который говорит, что класс обязательно реализует определенный функционал. И мы можем использовать это в своей программе. Например, определим следующий код:
Для отправки сообщения здесь определена функция sendMessage() , которая в качестве первого параметра принимает объект мессандера, а в качестве второго параметра — отправляемый текст. Причем определение первого параметра говорит, что передаваемое этому параметру значение должно реализовать интерфейс Messenger. В самой функции мы знаем, что первый параметр — это объект, который обязательно реализует интерфейс Messenger, поэтому мы можем вызвать у него метод send() для отправки сообщения:
Множественное применение интерфейсов
Класс может одновременно применять сразу несколько интерфейсов. В этом случае все интерфейсы перечисляются через запятую после слова implements . А класс должен реализовать методы всех применяемых интерфейсов:
PHP: Используем Интерфейсы для улучшения кода
Эта статья предназначена для разработчиков, которые имеют базовые представления о концепциях ООП (объектно-ориентированного программирования) и использовании наследования в PHP. Если вы знаете, как использовать наследование в своём PHP коде, мы надеемся, что эта статья будет понятной.
Что такое Интерфейсы
В общих чертах, интерфейсы — описание того, что ваш класс должен делать. Их можно использовать для гарантии, что любой класс реализующий интерфейс, будет включать каждый публичный метод определённый в интерфейсе.
Интерфейсы должны:
- Использоваться для определения публичных методов класса.
- Использоваться для определения констант класса.
Интерфейсы не должны:
- Использоваться сами по себе.
- Использоваться для определения приватных или защищённых методов класса.
- Использоваться для определения свойств класса.
Интерфейсы используются для определения публичных методов, которые должен включать класс. Важно помнить, что определены только сигнатуры методов и что они не включают тело метода (как вы обычно видите в методе класса). Это связано с тем, что интерфейсы используются только для определения взаимодействия между объектами, а не для определения взаимодействия и поведения, как в классе. Что дать немного контекста, в этом примере показан пример интерфейса, который определяет несколько общедоступных методов:
Согласно php.net, интерфейсы служат нескольким целям:
- Позволить разработчикам создавать объекты разных классов, которые могут использоваться взаимозаменяемо, поскольку они реализуют один и тот же интерфейс или интерфейсы. Типичный пример — несколько служб доступа к базе данных, несколько платёжных шлюзов или разные стратегии кэширования. Различные реализации могут быть заменены, не требуя каких-либо изменений в коде, который их использует.
- Позволить функции или методу принимать и оперировать параметром, который соответствует интерфейсу, не заботясь о том, что ещё может делать объект или как он реализован. Эти интерфейсы часто называют Iterable , Cacheable , Renderable и т.д., чтобы описать поведение.
Использование Интерфейсов в PHP
Интерфейсы могут быть бесценной частью кодовых баз ООП (объектно-ориентированного программирования). Они позволяют нам отделить наш код и улучшить расширяемость. Для иллюстрации этого, давайте рассмотрим следующий класс:
Как вы видите, мы определили класс с методом возвращающим строку. Таким образом, мы определили поведение метода, мы можем увидеть как getName() формирует возвращаемую строку. Однако допустим, что мы вызываем этот метод в нашем коде внутри другого класса. Другой класс не заботит то, как была сформирована эта строка, его заботит только то, что бы она была возвращена. Например, давайте посмотрим, как мы могли бы вызвать этот метод в другом классе:
Хотя вышеприведённый код работает, давайте представим, что мы хотим добавить функциональность для загрузки пользовательского отчёта из класса UserReport . Конечно, мы не можем использовать существующий метод в нашем ReportDownloadService , потому что мы установили, что можно передавать только класс BlogReport . Итак, нам нужно переименовать существующий метод, а затем добавить новый метод, как показано ниже:
Хотя вы этого не видите, давайте предположим, что остальные методы в приведённом выше классе используют идентичный код для создания загрузки. Мы могли бы превратить их общий код в методы, но, вероятно, у нас всё равно будет какой-то общий код. Кроме того, у нас будет несколько точек входа в класс, выполняющий почти идентичный код. Это потенциально может привести к дополнительной работе в будущем при попытке расширить код или добавить тесты.
Представим, что мы создаём новый AnalyticsReport ; нам нужно добавить в класс новый метод downloadAnalyticsReportPDF() . Как вы видите этот файл может начать быстро расти. Это может быть идеальным местом для использования интерфейсов.
Давайте создадим один; назовём его DownloadableReport и определим так:
Теперь вы можете обновить BlogReport и UsersReport для реализации интерфейса DownloadableReport , как показано в примере ниже. Но обратите внимание, я намеренно неправильно написал код для UsersReport , что бы кое-что продемонстрировать!
Если бы мы попытались запустить наш код, мы бы получили ошибки по следующим причинам:
- Пропущен метод getHeaders() .
- У метода getName() не задан тип возвращаемого значения. Но в сигнатуре метода интерфейса тип определён.
- У метода getData() задан тип возвращаемого значения, он не соответствует определённому в сигнатуре метода интерфейса.
Итак, что бы обновить UsersReport , для правильной реализации интерфейса DownloadableReport , нужно заменить его на следующий код:
Теперь, когда оба класса отчётов реализуют один и тот же интерфейс, мы можем обновить ReportDownloadService следующим образом:
Теперь мы можем передать объект UsersReport или BlogReport в метод downloadReportPDF() без каких либо ошибок. Потому что мы знаем, что необходимые методы нужные для классов отчёта существуют и возвращают данные ожидаемого типа.
В результате передачи интерфейса методу, а не классу, позволило нам слабо связать ReportDownloadService и классы отчётов в зависимости от того, что делают методы, а не от того, как они это делают.
Если бы мы захотели создать новый AnalyticsReport , мы могли бы заставить его реализовать тот же интерфейс, и это позволило бы нам передать объект отчёта в тот же метод downloadReportPDF() без необходимости добавлять какие-либо новые методы. Это может быть полезно, если вы хотите создать свой пакет или фреймворк, и хотите дать разработчику возможность создавать свой собственный класс. Вы можете просто сказать им, какой интерфейс реализовать, и они смогут создать собственный класс. Например, в Laravel вы можете создать собственный класс драйвера кэша, реализовав интерфейс Illuminate\Contracts\Cache\Store .
Помимо использования интерфейсов для улучшения актуального кода, мне нравятся интерфейсы, потому что они действуют как концепция «Документация — это код». Например, если я пытаюсь выяснить что может класс, а что не может, я обычно сначала смотрю на интерфейс, а затем на класс, который его реализует. Он сообщает вам все методы, которые могут быть вызваны, и не нужно заботиться о том, что у них под капотом.
Как заметили многие мои читатели Laravel-разработчик, довольно часто термины «контракт» и «интерфейс» используются как взаимозаменяемые. Согласно документации Laravel, контракты Laravel — это наборы интерфейсов, которые определяют основные сервисы предоставляемые фреймворком . Итак, важно помнить, что контракт — это интерфейс, но интерфейс необязательно является контрактом. Обычно контракт — это просто интерфейс, предоставляемый фреймворком. Для получения информации об использовании контрактов я рекомендую почитать документацию поскольку в ней хорошо разбирается то как их использовать и когда их использовать.
Вывод
Надеюсь, что прочитав эту статью вы получили краткий обзор того, что такое интерфейсы, как их использовать в PHP, об их преимуществах и использовании.