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

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

Чем отличается абстрактный класс от интерфейса? Технически, интерфейс может содержать только сигнатуры (определения) методов, а абстрактный класс может содержать реализацию, но главное отличие состоит в следующем: интерфейс служит для создания абстракции, а абстрактный класс, в большинстве случаев, предназначен для формирования структуры из классов с переносом одинакового функционала  в тело абстрактного класса. Возможно, звучит запутанно, но на деле все оказывается довольно просто. Давайте рассмотрим применение абстрактных классов на примере готовки трех видов пиццы: сырной, грибной и с колбасой. Для начала посмотрим на код без абстрактного класса:

<?php
 
class PizzaCheese
{
    private function prepareDough()
    {
        echo 'Подготовка теста' . PHP_EOL;
    }
 
    private function rollDough()
    {
        echo 'Раскатка теста' . PHP_EOL;
    }
 
    private function addStuff()
    {
        echo 'Добавляем заготовку начинки с сыром' . PHP_EOL;
    }
 
    private function baking()
    {
        echo 'Запекание' . PHP_EOL;
    }
 
    public function cook()
    {
        $this->prepareDough();
        $this->rollDough();
        $this->addStuff();
        $this->baking();
    }
}
 
class PizzaMushrooms
{
    private function prepareDough()
    {
        echo 'Подготовка теста' . PHP_EOL;
    }
 
    private function rollDough()
    {
        echo 'Раскатка теста' . PHP_EOL;
    }
 
    private function addStuff()
    {
        echo 'Добавляем заготовку начинки с грибами' . PHP_EOL;
    }
 
    private function baking()
    {
        echo 'Запекание' . PHP_EOL;
    }
 
    public function cook()
    {
        $this->prepareDough();
        $this->rollDough();
        $this->addStuff();
        $this->baking();
    }
}
 
class PizzaSausage
{
    private function prepareDough()
    {
        echo 'Подготовка теста' . PHP_EOL;
    }
 
    private function rollDough()
    {
        echo 'Раскатка теста' . PHP_EOL;
    }
 
    private function addStuff()
    {
        echo 'Добавляем заготовку начинки с колбасой' . PHP_EOL;
    }
 
    private function baking()
    {
        echo 'Запекание' . PHP_EOL;
    }
 
    public function cook()
    {
        $this->prepareDough();
        $this->rollDough();
        $this->addStuff();
        $this->baking();
    }
}
 
$pizzaCheese = new PizzaCheese();
$pizzaCheese->cook();
 
echo PHP_EOL;
 
$pizzaMushrooms = new PizzaMushrooms();
$pizzaMushrooms->cook();
 
echo PHP_EOL;
 
$pizzaSausage = new PizzaSausage();
$pizzaSausage->cook();

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

<?php
 
abstract class Pizza
{
    protected function prepareDough()
    {
        echo 'Подготовка теста' . PHP_EOL;
    }
 
    protected function rollDough()
    {
        echo 'Раскатка теста' . PHP_EOL;
    }
 
    abstract protected function addStuff();
 
    protected function baking()
    {
        echo 'Запекание' . PHP_EOL;
    }
 
    public function cook()
    {
        $this->prepareDough();
        $this->rollDough();
        $this->addStuff();
        $this->baking();
    }
}
 
class PizzaCheese extends Pizza
{
    protected function addStuff()
    {
        echo 'Добавляем заготовку начинки с сыром' . PHP_EOL;
    }
}
 
class PizzaMushrooms extends Pizza
{
    protected function addStuff()
    {
        echo 'Добавляем заготовку начинки с грибами' . PHP_EOL;
    }
}
 
class PizzaSausage extends Pizza
{
    protected function addStuff()
    {
        echo 'Добавляем заготовку начинки с колбасой' . PHP_EOL;
    }
}
 
$pizzaCheese = new PizzaCheese();
$pizzaCheese->cook();
 
echo PHP_EOL;
 
$pizzaMushrooms = new PizzaMushrooms();
$pizzaMushrooms->cook();
 
echo PHP_EOL;
 
$pizzaSausage = new PizzaSausage();
$pizzaSausage->cook();

Теперь повторяющиеся операции содержатся в одном месте и переносятся из базового абстрактного класса в конкретные реализации классов «выпекания» пиццы. Точно также, как и с интерфейсами, с внедрением абстрактных классов нужно быть внимательным, т.к. неумелое использование подобных инструментов может усложнить проект и запутать коллег. Когда стоит внедрять абстрактные классы в свой код? Если несколько классов, планируемых или существующих, выполняют часть одинаковых операций, причем, с объектами одного типа, то стоит.

Тонкости работы абстрактных классов на PHP можно узнать из документации.

Добавить комментарий