Абстрактная фабрика (Abstract Factory) паттерн

Отредачено 11.06.2025,
а чекнуто 528 💁

📌 Суть

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

Если обычная фабрика создает один объект — то абстрактная фабрика создает набор объектов, которые логически связаны и могут взаимодействовать друг с другом.

 

📦 Когда нужен этот паттерн?

Когда у тебя есть несколько реализаций целой системы или под-системы, и тебе нужно легко переключаться между ними.

 

🎯 Пример из жизни (Laravel Style)

Представь, ты делаешь систему уведомлений, которая может работать в разных каналах: emailsmsslack.

Каждый канал может состоять из нескольких компонентов:

  • Генератор шаблона

  • Отправщик

  • Логгер

И ты хочешь создать всё это в рамках одного семейства объектов.

 

Интерфейсы

interface NotificationSenderInterface {
    public function send(string $to, string $message): bool;
}

interface TemplateRendererInterface {
    public function render(string $template, array $data): string;
}

 

Реализации (Email)

class EmailSender implements NotificationSenderInterface {
    public function send(string $to, string $message): bool {
        // логика отправки email
        return true;
    }
}

class EmailTemplateRenderer implements TemplateRendererInterface {
    public function render(string $template, array $data): string {
        // подстановка данных в шаблон
        return "Email: " . strtr($template, $data);
    }
}

 

Реализации (SMS)

class SmsSender implements NotificationSenderInterface {
    public function send(string $to, string $message): bool {
        // логика отправки sms
        return true;
    }
}

class SmsTemplateRenderer implements TemplateRendererInterface {
    public function render(string $template, array $data): string {
        return "SMS: " . strtr($template, $data);
    }
}

 

Абстрактная фабрика

interface NotificationFactoryInterface {
    public function makeSender(): NotificationSenderInterface;
    public function makeRenderer(): TemplateRendererInterface;
}

 

Конкретные фабрики

class EmailNotificationFactory implements NotificationFactoryInterface {
    public function makeSender(): NotificationSenderInterface {
        return new EmailSender();
    }

    public function makeRenderer(): TemplateRendererInterface {
        return new EmailTemplateRenderer();
    }
}

class SmsNotificationFactory implements NotificationFactoryInterface {
    public function makeSender(): NotificationSenderInterface {
        return new SmsSender();
    }

    public function makeRenderer(): TemplateRendererInterface {
        return new SmsTemplateRenderer();
    }
}

 

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

function notify(NotificationFactoryInterface $factory, string $to, string $template, array $data)
{
    $sender = $factory->makeSender();
    $renderer = $factory->makeRenderer();

    $message = $renderer->render($template, $data);
    $sender->send($to, $message);
}

// Используем Email
$factory = new EmailNotificationFactory();
notify($factory, 'user@example.com', 'Привет, {{name}}', ['{{name}}' => 'Андрей']);

// Используем SMS
$factory = new SmsNotificationFactory();
notify($factory, '+71234567890', 'Привет, {{name}}', ['{{name}}' => 'Анна']);

 

Laravel-контекст

В Laravel ты можешь:

  • Создавать NotificationFactory как сервис, регистрировать в ServiceProvider.

  • Инъецировать нужную фабрику в зависимости от контекста (например, в зависимости от роли пользователя или источника запроса).

  • Применять Service Container, чтобы всё было прозрачно и гибко.

 

app()->bind(NotificationFactoryInterface::class, function () {
    return match (config('notification.driver')) {
        'email' => new EmailNotificationFactory(),
        'sms' => new SmsNotificationFactory(),
        default => throw new RuntimeException('Invalid notification driver'),
    };
});

 

Отличия от обычной фабрики

Критерий Обычная фабрика Абстрактная фабрика
Создаёт Один объект Набор логически связанных объектов
Интерфейс Один метод make() Несколько методов makeSender()makeRenderer() и т.д.
Когда использовать Простая логика создания Семейства компонентов, которые работают вместе
Пример в Laravel Model::factory() Каналы уведомлений, драйверы API, UI-компоненты
ООП Паттерны проектирования