Розробникам, які створюють додатки на Laravel, наполегливо рекомендують відмовитися від використання ключового слова new у своїх контролерах і перейти до патерну Factory Method. Перенесення створення об'єктів у спеціалізовані фабрики робить код легшим для розширення, тестування та підтримки — переваги, які стають критичними, коли проєкти розростаються за межі кількох ендпоінтів.
Чому ключове слово new є прихованою проблемою
Коли контролер містить такий рядок, як
$processor = new StripePaymentProcessor($config);
контролер стає жорстко пов'язаним із класом StripePaymentProcessor. Кожне місце, де потрібен процесор платежів, повторює цей рядок, розкидаючи логіку створення об'єктів по всьому коду. Якщо пізніше процесору знадобиться логер, менеджер кешу або інший формат конфігурації, доведеться оновлювати кожне таке місце. Результатом є крихкий код, який важко змінювати та складно підміняти (mock) у юніт-тестах.
Патерн Factory Method приходить на допомогу
Патерн Factory Method замінює пряму інстанціацію методом, який повертає екземпляр певного інтерфейсу. Контролер залежить від інтерфейсу, тоді як клас фабрики знає, який саме конкретний клас створити та як зібрати його залежності. Уся логіка створення зосереджена в одному місці, тому додавання нової залежності або заміна реалізації торкається лише фабрики.
Основні переваги
- Слабке зв'язування (Loose coupling) – Контролери працюють з абстракціями, а не з конкретними класами.
- Централізоване створення – Зміна процесу створення потребує редагування лише одного файлу.
- Тестованість – Фабрики можна підміняти стабами або моками, що дозволяє проводити ізольовані тести контролерів.
- Принцип відкритості/закритості (Open/Closed Principle) – Нові можливості (наприклад, новий платіжний шлюз) можна додавати, не змінюючи існуючий код контролера.
Аналогія з кав'ярнею
Уявіть бариста, який має вручну молоти зерна, збивати молоко та наливати воду для кожного замовлення, використовуючи довгу серію операторів if-else. Якщо рецепт лате зміниться, кожному бариста доведеться заново вивчати ці кроки. Кавомашина, яка отримує тип замовлення і сама займається приготуванням, вирішує цю проблему: бариста просто каже машині, що потрібно, а машина інкапсулює всі етапи. Тепер оновлення рецепта означає налаштування машини, а не перенавчання кожного бариста.
Laravel вже використовує фабрики
Основні компоненти самого Laravel демонструють цей патерн у дії:
- Database –
ConnectionFactoryвирішує, чи створювати підключення до MySQL, чи до PostgreSQL. - Queues –
QueueManagerстворює драйвери для Redis, SQS або інших бекендів. - Filesystem –
FilesystemManagerстворює екземпляри локальних дисків або дисків S3. - Mail –
MailManagerвизначає драйвери SMTP, Mailgun або інші поштові драйвери.
Якщо фреймворк довіряє фабрикам для цих критично важливих сервісів, то й власний код має працювати за тим самим принципом.
Коли варто впроваджувати фабрику
Використовуйте фабрику, коли:
- Створення об'єкта передбачає кілька етапів конфігурації або використання зовнішніх сервісів.
- Існують кілька взаємозамінних реалізацій (різні платіжні шлюзи, провайдери сховищ тощо).
- Очікується, що список можливих реалізацій зростатиме.
Уникайте фабрики, коли:
- Створення — це один незмінний виклик
newбез додаткового налаштування. - Буде використовуватися лише одна реалізація, що робить додаткову абстракцію непотрібним ускладненням.
Швидкий розбір: рефакторинг контролера платежів
- Визначте інтерфейс –
PaymentProcessorInterfaceз методомprocess(array $data). - Реалізуйте конкретні класи –
StripePaymentProcessor,PayPalPaymentProcessor, кожен з яких реалізує інтерфейс. - Створіть фабрику –
PaymentProcessorFactoryз методомmake(string $driver): PaymentProcessorInterface. Всередині використовуйтеswitchабо мапу для повернення відповідного класу, впроваджуючи необхідні сервіси з контейнера сервісів Laravel. - Впровадьте фабрику – У конструкторі контролера вкажіть тип
PaymentProcessorFactory. Laravel автоматично її розв'яже. - Використовуйте фабрику –
$processor = $this->processorFactory->make('stripe'); $processor->process($request->all());
Тепер контролер ніколи не згадує new або конкретний процесор. Додавання нового шлюзу означає створення класу та розширення мапи фабрики — без жодних змін у контролері.
Потенційні недоліки та способи їх усунення
Основною критикою фабрик є додана опосередкованість, що може здаватися надмірним для тривіальних об'єктів. Надмірне проєктування (over-engineering) простого сервісу може роздути кодову базу без реальної вигоди. Ключ полягає в оцінці складності конструювання: якщо об'єкт є простим носієм даних без залежностей, пряме використання new може бути прийнятним. Також фабрики можуть перетворитися на «смітник» для непов'язаної логіки. Зосередження фабрик на створенні об'єктів та делегування конфігурації спеціалізованим провайдерам сервісів допомагає зберегти чіткість.
На що звернути увагу далі
- Прив'язки контейнера сервісів – контейнер Laravel може автоматично розв'язувати фабрики, якщо ви прив'яжете інтерфейс до класу фабрики в провайдері сервісів.
- Автоматичне виявлення – деякі пакети надають власні фабрики; зважайте на можливі конфлікти імен.
- Стратегії тестування – під час модульного тестування контролерів замінюйте фабрику моком, який повертає заглушку процесора, щоб забезпечити швидкість та ізоляцію тестів.
Підсумок
Заміна розкиданих викликів new на доречно розміщену фабрику централізує конструювання, зменшує зв'язність і узгоджує ваш код із власною філософією проєктування Laravel. Для команд, які очікують на зростання — нові платіжні провайдери, сховища даних або будь-які компоненти плагінного типу — патерн Factory Method є малозатратною інвестицією, яка окупається гнучкістю та впевненістю. Ви, ваша майбутня версія, та кожен, хто успадкує цей код, подякують вам.
