Интерфейсы в ООП на PHP
Как вы уже знаете, абстрактные классы представляют собой набор методов для своих потомков. Часть этих методов может быть реализована в самом классе, а часть методов может быть объявлена абстрактными и требовать реализации в дочерних классах.
Представим себе ситуацию, когда ваш абстрактный класс представляет собой только набор абстрактных публичных методов, не добавляя методы с реализацией.
Фактически ваш родительский класс описывает интерфейс потомков, то есть набор их публичных методов, обязательных для реализации.
Зачем нам такое нужно: чтобы при программировании совершать меньше ошибок - описав все необходимые методы в классе-родителе, мы можем быть уверенны в том, что все потомки их действительно реализуют.
Когда это поможет: пусть мы создадим наш класс-родитель и несколько потомков к нему. Если потом через некоторое время, например, через месяц, мы решим создать еще одного потомка, наверняка мы уже забудем детали нашего кода и вполне можем забыть написать реализацию какого-нибудь метода в новом потомке. Однако сам PHP не позволит потерять метод - и просто выдаст ошибку.
То же самое касается другого программиста, работающего с вашим проектом. Пусть код класса-родителя писали вы, а затем ваш коллега решил создать еще одного потомка. У вашего коллеги также не получится потерять парочку методов.
Есть, однако, проблема: фактически мы сделали наш класс-родитель для того, чтобы писать в нем абстрактные публичные методы, но мы сами или наш коллега имеем возможность случайно добавить в этот класс не публичный метод или не абстрактный.
Пусть мы хотим физически запретить делать в родителе иные методы, кроме абстрактных публичных. В PHP для этого вместо абстрактных классов можно использовать интерфейсы.
Интерфейсы представляют собой классы, у которых все методы являются публичными и не имеющими реализации. Код методов должны реализовывать классы-потомки интерфейсов.
Интерфейсы объявляются так же, как и обычные
классы, но используя ключевое слово interface
вместо слова class
.
Для наследования от интерфейсов используется
немного другая терминология: говорят, что
классы не наследуют от интерфейсов, а реализуют
их. Соответственно вместо слова extends
следует использовать ключевое слово implements
.
Нельзя создать объект интерфейса. Все методы
интерфейса должны быть объявлены как public
и не должны иметь реализации. У интерфейса
могут быть только методы, но не свойства.
Нельзя также сделать интерфейс и класс с
одним и тем же названием.
Попробуем на практике
Давайте попробуем на практике. Решим задачу на фигуры из предыдущего рока, но уже используя интерфейсы, а не абстрактные классы.
Итак, теперь у нас дан интерфейс Figure
:
<?php
interface Figure
{
public function getSquare();
public function getPerimeter();
}
?>
Давайте напишем класс Quadrate
, который
будет реализовывать методы этого интерфейса:
<?php
class Quadrate implements Figure
{
private $a;
public function __construct($a)
{
$this->a = $a;
}
public function getSquare()
{
return $this->a * $this->a;
}
public function getPerimeter()
{
return 4 * $this->a;
}
}
?>
Как это работает: если забыть реализовать
какой-нибудь метод, описанный в интерфейсе,
PHP выдаст нам фатальную ошибку. Давайте
реализуем также класс Rectangle
:
<?php
class Rectangle implements Figure
{
private $a;
private $b;
public function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
}
public function getSquare()
{
return $this->a * $this->b;
}
public function getPerimeter()
{
return 2 * ($this->a + $this->b);
}
}
?>
Сделайте класс Disk
(круг),
реализующий интерфейс Figure
.
Замечание
Как уже было написано выше, не может быть
интерфейса и класса с одинаковым названием.
Это создает некоторые проблемы с придумыванием
названий. Например, мы хотим сделать класс
User
, реализующий интерфейс User
.
Как мы видим, у нас конфликт имен. Для его
разрешения, нужно или класс назвать по-другому,
или интерфейс.
Общепринято в таком случае название интерфейса
начать с маленькой буквы i
, чтобы
показать, что это интерфейс, а не класс.
То есть в нашем случае мы сделаем интерфейс
iUser
, а реализовывать его будет класс
User
. Такой подход мы иногда будем
применять в следующих уроках.