Шаблон проектирования «Фабричный метод» (Fabric Method)

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

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

Говоря простым языком, ИТ-рекрутер или HR-менеджер должен предоставить способ делегирования логики создания экземпляра дочерним классам.

Посмотрим, как это выглядит на примере PHP-кода. Поначалу у нас присутствует интерфейс Interviewer, а также несколько реализаций для этого интерфейса:

interface Interviewer
{
    public function askQuestions();
}

class Developer implements Interviewer
{
    public function askQuestions()
    {
        echo 'Спрашивает о шаблонах проектирования!';
    }
}

class CommunityExecutive implements Interviewer
{
    public function askQuestions()
    {
        echo 'Спрашивает про работу с сообществом';
    }
}

Давайте сейчас создадим нашего HiringManager:

abstract class HiringManager
{

    // Фабричный метод
    abstract public function makeInterviewer(): Interviewer;

    public function takeInterview()
    {
        $interviewer = $this->makeInterviewer();
        $interviewer->askQuestions();
    }
}

А теперь любой дочерний класс способен его расширять, предоставляя необходимого интервьюера:

class DevelopmentManager extends HiringManager
{
    public function makeInterviewer(): Interviewer
    {
        return new Developer();
    }
}

class MarketingManager extends HiringManager
{
    public function makeInterviewer(): Interviewer
    {
        return new CommunityExecutive();
    }
}

Вот, как это применяется:

$devManager = new DevelopmentManager();
$devManager->takeInterview(); // Вывод: Спрашивает о шаблонах проектирования!

$marketingManager = new MarketingManager();
$marketingManager->takeInterview(); // Вывод: Спрашивает про работу с сообществом

Когда полезен фабричный метод?

Итак, Fabric Method пригодится, если существует какая-то общая обработка в классе, причём необходимый подкласс динамически определяется в процессе выполнения. Говоря иначе, клиент не знает, какой конкретно подкласс ему может понадобиться.

Кстати, если интересует пример использования этого шаблона проектирования на языке Java, вам сюда.

По материалам статьи «Design Patterns for Humans».