Применение полиморфизма подтипов не убирает условные конструкции полностью (кроме некоторых случаев диспетчеризации, например, по ключу или по имени файла). Чаще условная конструкция остаётся одна, только на уровне выбора подходящей реализации, а вот затем эта реализация используется внутри полиморфной функции прямым способом без условий. В одном из прошлых уроков мы рассмотрели пример функции, которая выбирает необходимую реализацию стратегии на основе возраста пользователя и возвращает её наружу.
<?php
function chooseCostInsuranceStrategy($user)
if ($user->getAge() < 18) {
return new LessThan18();
} else if (/* ... */) {
// some code
}
}
Функция, которая: выбирает нужный класс, создает объект и возвращает его наружу, называется фабрикой (более точно фабричным методом). Громкое имя для очень простой штуки. Фабрика может быть реализована любым способом включая всё, что разбиралось в этом курсе.
В общем случае фабрикой называют всё подряд, что создает объект или коллекции объектов. Причём не обязательно разных классов, класс может быть и один, но сам процесс создания включает в себя какие-то предварительные вычисления. В реальных проектах фабрики могут быть огромными.
Фабрики часто реализуются как классы с одним статическим методом – factory
. Сами фабрики объектами не делают (иногда таки делают), так как это не абстракция данных, а подменять их смысла нет, иначе получится подмена подменятора.
Диспетчеризация класса
PHP позволяет создавать объекты, используя имя класса как строку:
<?php
$className = 'stdObject';
$obj = new $className();
В большинстве других динамических языков, класс и так объект первого рода, и им можно пользоваться как обычными данными.
Такой синтаксис открывает широкий простор для диспетчеризации. Например, в некоторых ситуациях получится уйти от условных конструкций вообще:
<?php
// Policy – обычно это имя используют для авторизации, то есть системы проверки прав доступа
function getUserPolicy($user)
{
$mapping = [
'manager' => 'App\Policies\ManagerPolicy',
'worker' => 'App\Policies\WorkerPolicy'
];
$className = $mapping[$user->getType()];
return new $className();
}
В этом коде есть одна небольшая проблема. Она связана с тем, что нужно указывать полное имя класса, включая все пространства имён, иначе этот код не заработает. От неё можно уйти если использовать специальный синтаксис извлечения полного имени класса:
<?php
use App\Policies\ManagerPolicy;
use App\Policies\WorkerPolicy;
$mapping = [
'manager' => ManagerPolicy::class, // App\Policies\ManagerPolicy
'worker' => WorkerPolicy::class // App\Policies\WorkerPolicy
];
То есть достаточно после имени любого класса добавить ::class
и получится выражение, которое возвращает полное имя класса в виде строки.
Иногда идут ещё дальше и формируют само имя класса динамически. Это можно сделать даже с кодом выше. По нему видно, что имя класса однозначно выводится из типа пользователя. Ниже пример, в котором фабрика вообще не содержит конкретных имён классов, она выводит их на основе соглашения об именовании: type => typePolicy.
<?php
// Policy – обычно это имя используют для авторизации, то есть системы проверки прав доступа
function getUserPolicy($user)
{
// При таком подходе придётся самостоятельно добавлять все неймспейсы
// Но зато код полностью отвязан от имён классов и их количества
$className = 'App\\Policies\\' . ucfirst($user->getType()) . 'Policy';
return new $className();
}
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.