Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Интерфейсы и абстрактные классы

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

Интерфейс

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

<?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() вызовет ошибку. Объекты создают только из обычных классов, которые их реализуют.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.