Очистіть свій Java код за допомогою патерну Abstract Factory
Припиніть дублювати створення об’єктів і розблокуйте масштабовані комбінації пов’язаних об’єктів, таких як Email + SMS сповіщення.
Проблема: Жорстка і повторювана логіка сповіщень
Припустимо, ви створюєте систему, яка надсилає різні типи сповіщень. З часом впроваджуються нові типи сповіщень, які поєднують різні канали:
- ALERT: Надсилає і Email, і SMS
- NEWSLETTER: Надсилає лише Email
- PROMOTION: Надсилає лише SMS
Спочатку ви можете написати щось подібне:
public class NotificationService {
public void send(String type, String message) {
if (type.equals("ALERT")) {
Notification email = new EmailNotification();
Notification sms = new SMSNotification();
email.notifyUser(message);
sms.notifyUser(message);
} else if (type.equals("NEWSLETTER")) {
Notification email = new EmailNotification();
email.notifyUser(message);
} else if (type.equals("PROMOTION")) {
Notification sms = new SMSNotification();
sms.notifyUser(message);
} else {
throw new IllegalArgumentException("Unknown type: " + type);
}
}
public static void main(String[] args) {
NotificationService service = new NotificationService();
service.send("ALERT", "Server is down!");
service.send("NEWSLETTER", "June updates are here!");
}
}
Що тут не так?
- Код повторюється — створення сповіщень вручну кожного разу.
- Порушується принцип відкритості/закритості — нові типи вимагають модифікації методу send.
- Важко тестувати — тісне зв’язування ускладнює створення моків або розширення.
- Низька масштабованість — що, якщо пізніше потрібно буде надсилати Slack, Push або WhatsApp?
Рішення: Використовуйте патерн Abstract Factory
Abstract Factory допомагає, коли потрібно створювати сімейства пов’язаних об’єктів — наприклад, «комбо» сповіщень, які йдуть разом.
Крок 1: Визначте спільний інтерфейс
public interface Notification {
void notifyUser(String message);
}
Крок 2: Реалізуйте конкретні сповіщення
public class EmailNotification implements Notification {
@Override
public void notifyUser(String message) {
System.out.println("Sending EMAIL: " + message);
}
}
public class SMSNotification implements Notification {
@Override
public void notifyUser(String message) {
System.out.println("Sending SMS: " + message);
}
}
Крок 3: Створіть абстрактну фабрику
public interface NotificationAbstractFactory {
Notification createPrimaryNotification();
Notification createSecondaryNotification();
}
Крок 4: Створіть конкретні фабрики
public class AlertNotificationFactory implements NotificationAbstractFactory {
public Notification createPrimaryNotification() {
return new EmailNotification();
}
public Notification createSecondaryNotification() {
return new SMSNotification();
}
}
public class NewsletterNotificationFactory implements NotificationAbstractFactory {
public Notification createPrimaryNotification() {
return new EmailNotification();
}
public Notification createSecondaryNotification() {
return null;
}
}
public class PromotionNotificationFactory implements NotificationAbstractFactory {
public Notification createPrimaryNotification() {
return new SMSNotification();
}
public Notification createSecondaryNotification() {
return null;
}
}
Крок 5: Використовуйте фабрику у вашому сервісі
public class NotificationService {
public void send(NotificationAbstractFactory factory, String message) {
Notification primary = factory.createPrimaryNotification();
Notification secondary = factory.createSecondaryNotification();
primary.notifyUser(message);
if (secondary != null) {
secondary.notifyUser(message);
}
}
public static void main(String[] args) {
NotificationService service = new NotificationService();
service.send(new AlertNotificationFactory(), "Server is down!");
service.send(new NewsletterNotificationFactory(), "Check out our new features!");
service.send(new PromotionNotificationFactory(), "50% OFF this weekend!");
}
}
Чому це працює краще
- Масштабованість: Додавайте нові комбінації, просто додаючи нову фабрику — без змін у логіці.
- Тестованість: Ви можете створювати моки або заглушки для фабрики та сповіщень для юніт-тестів.
- Слідує принципу відкритості/закритості: Ваша основна логіка залишається недоторканою.
- Чисте розділення обов’язків: Логіка створення живе у фабриках, а не в сервісних класах.
Бонус: Ви все ще можете використовувати провайдер фабрик
Якщо ви хочете динамічно вибирати фабрику на основі рядка або конфігурації:
public class NotificationFactoryProvider {
public static NotificationAbstractFactory getFactory(String type) {
return switch (type) {
case "ALERT" -> new AlertNotificationFactory();
case "NEWSLETTER" -> new NewsletterNotificationFactory();
case "PROMOTION" -> new PromotionNotificationFactory();
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}
}
Використання:
NotificationAbstractFactory factory = NotificationFactoryProvider.getFactory("ALERT");
service.send(factory, "System is overheating!");
Заключні думки
Abstract Factory — це чудова еволюція за межі простого Factory Method, коли ви маєте справу з комбінаціями об’єктів або сімействами пов’язаних продуктів. Це особливо корисно в:
- Системах сповіщень
- Темізації UI (наприклад, створення узгоджених темних/світлих віджетів)
- Крос-платформних компонентах
Якщо ваш код починає виглядати як ліс блоків if-else зі створенням об’єктів, захардкодженим всередині них — можливо, настав час для патерну Abstract Factory.
Оригінал статті тут: medium.com/...tory-pattern-0f0c48e0ef96
Відгуки та коментарі вітаються! Буду дуже радий зворотньому зв’язку!

19 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів