Дуже дякую за відгук. З назвами мабуть дійно треба допрацювати. Це планувалось більше як навчальна стаття для новачків.
Так, ваша версія абсолютно робоча — і для більшості продакшн-завдань такий підхід (з createNotification(...) як утилітним методом) цілком достатній. Але тут є нюанс: це не Factory Method у сенсі патерну з GoF, а просто зовнішня фабрика або «ручна інстанціація з логікою вибору».
Factory Method як патерн — це не просто метод, що повертає об’єкт. Це метод, який є частиною абстрактного класу або інтерфейсу, і який переозначується в підкласах для контролю створення конкретного об’єкта.
У вашому прикладі createNotification(...) — це procedural-style фабрика (схоже на Simple Factory або static factory), а от класична реалізація Factory Method виглядає саме так:
abstract class NotificationFactory {
public abstract Notification createNotification();
}
class EmailNotificationFactory extends NotificationFactory {
public Notification createNotification() {
return new EmailNotification();
}
}
І вже клієнт викликає factory.createNotification() без знання про конкретний клас.
Основна користь цього — інверсія залежностей + можливість розширення без модифікації коду, що відповідає принципу відкритості/закритості (OCP з SOLID). А в деяких випадках — ще й впровадження додаткової поведінки під час створення (логування, валідація, lazy init тощо).
Дякую за коментар. Про абстрактну фабрику буде стаття завтра в 11:20. Там теж цікаво і чекаю ваші коментарі. Паттерн абстрактної фабрики використовується для створення сімей пов’язаних об’єктів (наприклад, Notification + Template + Logger). Тут в Factory Method створюється один тип продукту.
Дякую за коментар — видно, що ви добре розбираєтесь у темі.
Власне, мета прикладу була не побудувати ідеальну продакшн-архітектуру, а показати базову суть патерну Factory Method «в чистому вигляді» — з Product, Creator, конкретними фабриками і розділенням створення та використання. Це типова демонстрація структури з книжки GoF, без DI, енумів і автоматичної магії.
Так, погоджуюсь — уже на етапі виділення інтерфейсів (Notification) і реалізацій (EmailNotification/SMSNotification) ми вирішуємо більшість проблем початкового коду.
А далі вже йде трохи «показушна» декомпозиція — щоб новачки зрозуміли, де в цьому патерн, а не просто «як краще зробити код».
Про DI — абсолютно згоден, у реальних проєктах зараз або Spring, або інші контейнери вирішують створення об’єктів фабриками за нас. Але для цього теж треба спочатку зрозуміти, що саме вони приховують.
Тому цей приклад — це радше «навчальний стенд», ніж готовий шматок для реального застосування. Але слушна думка — можна зробити окремий матеріал, як Factory Method виглядає в сучасному
Дякую за детальну критику! Ви абсолютно праві щодо теорії.
Але я свідомо вибрав цей підхід для навчання. Ось чому:
Чому так краще для засвоєння:
Від практичної проблеми до рішення — так людина краще розуміє навіщо патерн потрібен. Замість абстрактного «створюй сімейства об’єктів» показую реальний біль: «дивись, як твій код перетворюється на лапшу»
Еволюція мислення — показую весь шлях від копіпасти до структурованого коду. Людина бачить не просто «ось правильний код», а процес покращення
Не лякає новачків складними прикладами з UI або ігровими кімнатами. Сповіщення — це те, з чим всі стикалися. Зрозуміло з першого погляду
Мотивація через біль — спочатку показую як погано, потім як добре. Це створює «ага-момент»
Ваш чистий приклад з AlertEmail/NewsletterEmail — теоретично правильний, але для початківця незрозуміло навіщо робити різні реалізації Email. Він подумає «а чим AlertEmail відрізняється від звичайного Email?»
Мій підхід — поетапний:
Показую конкретну проблему (дублювання коду) — людина одразу розуміє біль
Демонструю механіку винесення створення в фабрики — основна ідея патерну
Після засвоєння можна переходити до класичних прикладів з правильними сімействами
Психологія навчання: краще спочатку зрозуміти навіщо і як працює патерн на простому прикладі, а потім вивчати правильні нюанси і терміни. Ніж одразу давати «правильний» але складний код, який відлякує.
Моя мета — щоб після читання людина подумала: «Ого, а в мене теж така лапша в коді! Треба виправити!» А не «Складно, не зрозуміло, пропускаю».
Після засвоєння цього прикладу людина легше зрозуміє і ваші класичні варіанти з UI темами чи ігровими кімнатами.