Интерфейсы объектов позволяют создавать код, который указывает, какие методы должен реализовать класс, без необходимости определять, как именно они должны быть реализованы. Интерфейсы разделяют пространство имён с классами и трейтами, поэтому они не могут называться одинаково.
Интерфейсы объявляются так же, как и обычные классы, но с использованием ключевого слова interface вместо class. Тела методов интерфейсов должны быть пустыми.
Все методы, определённые в интерфейсах, должны быть общедоступными, что следует из самой природы интерфейса.
На практике интерфейсы используются в двух взаимодополняющих случаях:
-
Чтобы позволить разработчикам создавать объекты разных классов, которые могут использоваться взаимозаменяемо, поскольку они реализуют один и тот же интерфейс или интерфейсы. Типичный пример — несколько служб доступа к базе данных, несколько платёжных шлюзов или разных стратегий кеширования. Различные реализации могут быть заменены без каких-либо изменений в коде, который их использует.
-
Чтобы разрешить функции или методу принимать и оперировать параметром, который соответствует интерфейсу, не заботясь о том, что ещё может делать объект или как он реализован. Эти интерфейсы часто называют Iterable, Cacheable, Renderable и так далее, чтобы описать их поведение.
Интерфейсы могут определять магические методы, требуя от реализующих классов реализации этих методов.
Замечание:
Хотя они поддерживаются, использование конструкторов в интерфейсах настоятельно не рекомендуется. Это значительно снижает гибкость объекта, реализующего интерфейс. Кроме того, к конструкторам не применяются правила наследования, что может привести к противоречивому и неожиданному поведению.
Ключевое слово implements
Для реализации интерфейса используется оператор implements. Класс должен реализовать все методы, описанные в интерфейсе, иначе произойдёт фатальная ошибка. При желании классы могут реализовывать более одного интерфейса, разделяя каждый интерфейс запятой.
Класс может реализовать два интерфейса, которые определяют метод с тем же именем, только если объявление метода в обоих интерфейсах идентично.
Класс, реализующий интерфейс, может использовать для своих параметров имя, отличное от имени интерфейса. Однако, начиная с PHP 8.0, в языке поддерживаются именованные аргументы, и вызывающий код может полагаться на имя параметра в интерфейсе. По этой причине настоятельно рекомендуется, чтобы разработчики использовали те же имена параметров, что и реализуемый интерфейс.
Интерфейсы могут быть унаследованы друг от друга, так же, как и классы, с помощью оператора extends.
Класс, реализующий интерфейс, должен объявить все методы в интерфейсе с совместимой сигнатурой.
Константы (Constants)
Интерфейсы могут содержать константы. Константы интерфейсов работают точно так же, как и константы классов, за исключением того, что они не могут быть переопределены наследующим классом или интерфейсом.
Примеры
Пример #1 Пример интерфейса
<?php // Объявим интерфейс '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); } return $template; } } // Это не будет работать // (Фатальная ошибка: Класс 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() { } public function baz(Foo $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() { } public function baz() { } } ?>
Пример #4 Интерфейсы с константами
<?php interface A { const B = 'Константа интерфейса'; } // Выведет: Константа интерфейса echo A::B; // Это, однако, не будет работать, так как // константы переопределять нельзя. class B implements A { const B = 'Константа класса'; } ?>
Пример #5 Интерфейсы с абстрактными классами
<?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; } } $c = new C(); echo $c->bar(2); // OK, 4 $b = new B(); $b->foo("123"); // error: Cannot instantiate abstract class B ?>
Пример #6 Одновременное расширение и внедрение
<?php class One { /* ... */ } interface Usable { /* ... */ } interface Updatable { /* ... */ } // Порядок ключевых слов здесь важен. "extends" должно быть первым. class Two extends One implements Usable, Updatable { /* ... */ } ?>
Интерфейс, совместно с объявлениями типов, предоставляет отличный способ проверки того, что определённый объект содержит определённый набор методов. Смотрите также оператор instanceof и объявление типов.
Источники:
(1) https://www.php.net/manual/ru/language.oop5.interfaces.php
Нет Ответов