Интерфейсы и абстрактные классы
Иногда важно не то, как устроен объект, а что он умеет делать. Интерфейс описывает именно это: список методов, которые класс обязан иметь.
Интерфейс
В интерфейсе перечислены методы без тела. Класс, который его реализует, обязан объявить их все:
<?php
interface Shape
{
public function area(): float;
}
class Rectangle implements Shape
{
public function __construct(private float $width, private float $height) {}
public function area(): float
{
return $this->width * $this->height;
}
}
class Circle implements Shape
{
public function __construct(private float $radius) {}
public function area(): float
{
return 3.14159 * $this->radius ** 2;
}
}
function printArea(Shape $shape): void
{
echo $shape->area(), "\n";
}
printArea(new Rectangle(2, 3)); // 6
printArea(new Circle(1)); // 3.14159Функция printArea() принимает любой объект, реализующий Shape. Ей не важно, прямоугольник это или круг, — достаточно, что у него есть метод area().
Зачем это нужно
Интерфейс — это договор между частями программы. Добавив новую фигуру, вы не меняете printArea(): достаточно реализовать интерфейс. Так программу проще расширять и тестировать.
Встроенные интерфейсы
В PHP есть готовые интерфейсы, которые связывают ваши классы с возможностями языка. Например, Countable позволяет передавать объект в функцию count():
<?php
class Cart implements Countable
{
private array $items = [];
public function add(string $item): void
{
$this->items[] = $item;
}
public function count(): int
{
return count($this->items);
}
}
$cart = new Cart();
$cart->add('чай');
$cart->add('кофе');
echo count($cart); // 2Класс может реализовать несколько интерфейсов сразу — их перечисляют через запятую.
Абстрактный класс
Абстрактный класс — это заготовка: часть методов в нём готова, а часть только объявлена и должна появиться в наследниках:
<?php
abstract class Report
{
abstract protected function body(): string;
public function render(): string
{
return "=== Отчёт ===\n" . $this->body();
}
}
class SalesReport extends Report
{
protected function body(): string
{
return 'Продано: 42';
}
}
echo (new SalesReport())->render();Общая часть — заголовок — написана один раз, а содержимое каждый отчёт определяет сам.
Что выбрать
| Интерфейс | Абстрактный класс | |
|---|---|---|
| Готовый код | Нет | Может быть |
| Свойства | Нет | Есть |
| Сколько можно указать | Несколько | Один |
Если нужно только описать, что объект умеет, — берите интерфейс. Если у наследников есть общий код — абстрактный класс.
Частая ошибка
Создать объект интерфейса или абстрактного класса нельзя: запись new Shape() вызовет ошибку. Объекты создают только из обычных классов, которые их реализуют.