Абстрактные классы

Может ли нам понадобится когда-нибудь создавать объекты класса HTMLElement? Наверняка нет. Вся работа строится на базе конкретных элементов, а значит, на практике, всегда используются его наследники.

В PHP классы, объекты которых не имеют смысла, принято помечать специальным ключевым словом abstract. Тогда никто не сможет создать объект этого класса напрямую. Только через наследников. В остальном, абстрактный класс такой же класс, со своими методами и свойствами.

<?php

abstract class HTMLElement
{
    public $attributes = [];

    public function __construct($attributes = [])
    {
        $this->attributes = $attributes;
    }

    public function getAttribute(string $key)
    {
        return $this->attributes[$key];
    }
}

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

Ещё одна особенность абстрактных классов связана с использованием интерфейсов. Абстрактный класс, в отличие от конкретного, не должен реализовывать интерфейсы полностью. Всё, что не реализовывает абстрактный класс, должны реализовать его наследники. Рассмотрим конкретный пример. Каждый объект, описывающий конкретный DOM-элемент, имеет текстовое представление в виде HTML-тега. Это значит, что каждый класс, наследующий HTMLElement, должен реализовывать определённый метод, возвращающий кусок HTML. В такой ситуации можно ввести специальный интерфейс Showable (обычно так называют интерфейсы, которые отвечают за строковое представление), содержащий один метод __toString().

<?php

interface Showable
{
    public function __toString();
}

И использование:

<?php

abstract class HTMLElement implements Showable // __toString
{
    // здесь нет реализации __toString
}

С одной стороны здесь написано что абстрактный класс реализует интерфейс, с другой, так как класс абстрактный, мы можем не реализовывать Showable напрямую. Но это обязательно придётся сделать в наследниках, что нам и нужно.

Абстрактные классы могут иметь абстрактные методы, фактически это сигнатуры методов, а не сами методы. В этом смысле они становятся похожи на интерфейсы.

<?php

abstract class HTMLElement
{
    abstract public function __toString();
}

С появлением абстрактных классов возникает множество новых вопросов: когда они нужны а когда нет, можно ли использовать абстрактные классы вместо интерфейсов, как совмещать их с интерфейсами и так далее?

Абстрактные классы не фундаментальная концепция и не обязательная часть ООП. Более того, в большинстве ООП языков абстрактных классов нет. Однако нельзя сказать что код на них пишется хуже, скорее наоборот. Например в Ruby, JavaScript и Python абстрактных классов нет, но есть наследование и есть классы, которые можно было отметить как абстрактные, однако создатели этих языков не стали вводить это понятие в язык. Никогда нельзя забывать, что любые новые сущности, упрощая в одном месте, увеличивают общую сложность из-за возрастающего числа комбинаций этих сущностей друг с другом.

Общая рекомендация, не придавайте слишком много значения абстрактным классам. Используйте их тогда, когда класс имеет смысл пометить абстрактным, но не более того. Как вы увидите позже, абстрактные классы, вместе с наследованием, успешно заменяются трейтами. Причём последние гораздо более гибкий инструмент.

Мы учим программированию с нуля до стажировки и работы. Попробуйте наш бесплатный курс «Введение в программирование» или полные программы обучения по Javascript, PHP, Python и Java.

Хекслет

Подробнее о том, почему наше обучение работает →