В чём разница между Factory Method и Abstract Factory?
Оба паттерна помогают создавать объекты, но на разных уровнях:
🟢 Junior Level
Оба паттерна помогают создавать объекты, но на разных уровнях:
Factory Method — создаёт один тип объектов:
// Фабричный метод создаёт один продукт
interface Button { void render(); }
class WinButton implements Button { ... }
class MacButton implements Button { ... }
abstract class Dialog {
// Factory Method — подкласс решает, какую кнопку создать
abstract Button createButton();
}
Abstract Factory — создаёт семейство связанных объектов:
// Абстрактная фабрика создаёт целое семейство продуктов
// Связанных = они должны быть из одной «семьи» (все Windows-компоненты или все Mac-компоненты),
// иначе UI будет выглядеть неконсистентно.
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
Menu createMenu();
}
class WinFactory implements GUIFactory {
Button createButton() { return new WinButton(); }
Checkbox createCheckbox() { return new WinCheckbox(); }
Menu createMenu() { return new WinMenu(); }
}
Главная разница:
- Factory Method = один продукт (кнопка)
- Abstract Factory = семейство продуктов (кнопка + чекбокс + меню)
🟡 Middle Level
OCP (Open/Closed Principle) — открыт для расширения, закрыт для модификации.
Factory Method (Фабричный метод)
Механизм: Наследование (подклассы решают)
// Abstract class с Factory Method
public abstract class PaymentService {
// Шаблонный метод
public void processPayment() {
PaymentGateway gateway = createGateway(); // Factory Method
gateway.authorize();
gateway.capture();
}
// Подкласс реализует создание
abstract PaymentGateway createGateway();
}
// Конкретные реализации
public class CreditCardService extends PaymentService {
@Override
PaymentGateway createGateway() {
return new StripeGateway(); // Решает подкласс
}
}
public class PayPalService extends PaymentService {
@Override
PaymentGateway createGateway() {
return new PayPalGateway();
}
}
Когда использовать:
- Нужно расширять систему новыми типами
- Класс не знает, какие объекты создавать
- Создание делегируется подклассам
Abstract Factory (Абстрактная фабрика)
Механизм: Композиция (объекты передаются)
// Интерфейс фабрики
public interface DatabaseFactory {
Connection createConnection();
Statement createStatement();
ResultSet createResultSet();
}
// Конкретная фабрика для MySQL
public class MySQLFactory implements DatabaseFactory {
public Connection createConnection() { return new MySQLConnection(); }
public Statement createStatement() { return new MySQLStatement(); }
public ResultSet createResultSet() { return new MySQLResultSet(); }
}
// Конкретная фабрика для PostgreSQL
public class PostgresFactory implements DatabaseFactory {
public Connection createConnection() { return new PostgresConnection(); }
public Statement createStatement() { return new PostgresStatement(); }
public ResultSet createResultSet() { return new PostgresResultSet(); }
}
// Клиент работает с любой фабрикой
public class DatabaseService {
private final DatabaseFactory factory;
public DatabaseService(DatabaseFactory factory) {
this.factory = factory; // Передаём нужную фабрику
}
public void query() {
Connection conn = factory.createConnection();
Statement stmt = factory.createStatement();
// Все объекты совместимы!
}
}
Когда использовать:
- Нужно создать семейство связанных объектов
- Важно, чтобы объекты были из одного семейства
- Клиент не должен знать о конкретных классах
Сравнение
| Критерий | Factory Method | Abstract Factory |
|---|---|---|
| Что создаёт | Один объект | Семейство объектов |
| Механизм | Наследование | Композиция |
| Расширение | Новый подкласс | Новая фабрика |
| Сложность | Низкая | Высокая |
| OCP | ✅ Соблюдается | ❌ Нарушается при добавлении продукта |
Типичные ошибки
- Abstract Factory для одного продукта
// ❌ Overengineering interface ButtonFactory { Button create(); } // ✅ Достаточно Factory Method abstract class Dialog { abstract Button createButton(); } - Нарушение OCP в Abstract Factory
// Добавили новый продукт → ломаем ВСЕ фабрики interface GUIFactory { Button createButton(); Checkbox createCheckbox(); Tooltip createTooltip(); // ← Новое! } // Все реализации нужно изменить!
🔴 Senior Level
Architectural Scope
Factory Method = точка расширения в иерархии:
Dialog (абстракция)
├── createButton() ← точка расширения
│ ├── WinButton
│ └── MacButton
└── Шаблонный метод использует createButton()
Abstract Factory = граница совместимости:
GUIFactory (граница семейства)
├── WinFactory → WinButton + WinCheckbox + WinMenu
└── MacFactory → MacButton + MacCheckbox + MacMenu
Гарантия: продукты из разных семейств НЕ смешиваются!
Open/Closed Principle Trade-offs
Factory Method соблюдает OCP:
// Добавить новый тип → новый подкласс (не меняем существующий код)
class CryptoService extends PaymentService {
@Override
PaymentGateway createGateway() {
return new CoinbaseGateway();
}
}
// ✅ Existing code не изменён
Abstract Factory нарушает OCP при расширении:
// Добавить новый продукт → изменить интерфейс → сломать ВСЕ реализации
interface GUIFactory {
Button createButton();
Slider createSlider(); // ← Новый продукт
}
// Все фабрики нужно обновить:
class WinFactory implements GUIFactory {
Slider createSlider() { return new WinSlider(); } // ← Добавить!
}
class MacFactory implements GUIFactory {
Slider createSlider() { return new MacSlider(); } // ← Добавить!
}
// ❌ Existing code изменён
Эволюция системы
Simple Factory (статический метод)
↓ (нужна расширяемость)
Factory Method (наследование)
↓ (нужно несколько продуктов)
Abstract Factory (семейства)
↓ (нужна гибкость)
DI Container (Spring)
Simple Factory vs Factory Method
Simple Factory = статический метод. Нельзя расширить наследованием. Factory Method = виртуальный метод в подклассе. Можно расширять.
Modern Java реализации
Factory Method → Supplier:
// Вместо наследования
public class Dialog {
private final Supplier<Button> buttonFactory;
public Dialog(Supplier<Button> buttonFactory) {
this.buttonFactory = buttonFactory;
}
public void render() {
Button button = buttonFactory.get();
button.render();
}
}
// Использование
Dialog dialog = new Dialog(WinButton::new);
Abstract Factory → Records:
// Record для хранения семейства
public record GUIFactory(
Supplier<Button> button,
Supplier<Checkbox> checkbox,
Supplier<Menu> menu
) {}
// Предопределённые фабрики
public static final GUIFactory WINDOWS = new GUIFactory(
WinButton::new, WinCheckbox::new, WinMenu::new
);
public static final GUIFactory MAC = new GUIFactory(
MacButton::new, MacCheckbox::new, MacMenu::new
);
Spring Context как Meta-Factory
// Spring сам решает, какие бины создать
@Configuration
public class DatabaseConfig {
@Bean
@Profile("mysql")
public DatabaseFactory mysqlFactory() {
return new MySQLFactory();
}
@Bean
@Profile("postgres")
public DatabaseFactory postgresFactory() {
return new PostgresFactory();
}
}
// Spring = Abstract Factory на стероидах
// + Profile = выбор семейства на runtime
Performance Implications
Factory Method:
Monomorphic call (1 подкласс) → инлайнинг
Bimorphic call (2 подкласса) → всё ещё быстро
Megamorphic call (>2 подклассов) → косвенный вызов
Abstract Factory:
Дополнительный уровень косвенности:
client → factory interface → concrete factory → product
Накладные расходы: 2 виртуальных вызова вместо 1
Production Experience
Реальный сценарий: Abstract Factory спас от mixing
- UI фреймворк: Windows и Mac темы
- Проблема: разработчики смешивали компоненты
- Решение: Abstract Factory гарантирует совместимость
- Результат: невозможно создать MacButton в Windows теме
Best Practices
- Factory Method для одного расширяемого продукта
- Abstract Factory для семейства связанных продуктов
- **Supplier
** вместо Factory Method в Modern Java
**Supplier
- Spring Profiles вместо ручных Abstract Factory
- Избегайте Abstract Factory если семейство одно
- Помните про OCP violation при расширении Abstract Factory
- Record для хранения семейства фабрик
Records — Java 16+ компактные immutable классы.
Резюме для Senior
- Factory Method = наследование, один продукт, OCP-friendly
- Abstract Factory = композиция, семейство продуктов, OCP violation
- Эволюция: Simple → Factory Method → Abstract Factory → DI
- Modern Java: Supplier
заменяет Factory Method - Spring = Meta-Factory с Profiles
- OCP Trade-off: Добавление нового продукта в Abstract Factory требует изменения ВСЕХ реализаций фабрик. Если фабрик 10 — 10 файлов нужно править.
- Guarantee: Abstract Factory предотвращает mixing продуктов
🎯 Шпаргалка для интервью
Обязательно знать:
- Factory Method создаёт один продукт через наследование, Abstract Factory — семейство продуктов через композицию
- Factory Method соблюдает OCP (новый подкласс не меняет код), Abstract Factory нарушает OCP при добавлении нового продукта
- Эволюция: Simple Factory → Factory Method → Abstract Factory → DI Container (Spring)
- В Modern Java: Factory Method заменяется Supplier
, Abstract Factory — Records или Map<Enum, Supplier> - Spring Context = Meta-Factory с @Profiles для выбора семейства на runtime
- Megamorphic calls: >2 подклассов в Factory Method → JIT не может инлайнить
- Abstract Factory гарантирует: продукты из разных семейств НЕ смешиваются
Частые уточняющие вопросы:
- Когда выбрать Factory Method, а когда Abstract Factory? — Один продукт = Factory Method, семейство связанных = Abstract Factory
- Почему Abstract Factory нарушает OCP? — Добавление нового продукта требует изменения интерфейса → ВСЕ реализации нужно править
- Чем Simple Factory отличается от Factory Method? — Simple Factory = статический метод (нельзя расширить), Factory Method = виртуальный метод в подклассе
- Как Spring заменяет Abstract Factory? — @Configuration + @Profile = выбор семейства бинов на runtime
Красные флаги (НЕ говорить):
- “Я использую Abstract Factory для одного продукта” — overengineering, достаточно Factory Method
- “Factory Method и Abstract Factory — это одно и то же” — принципиально разные механизмы и цели
- “Abstract Factory соблюдает OCP” — нарушает при добавлении нового продукта
- “В Spring не нужны Factory паттерны” — Spring сам использует Abstract Factory под капотом
Связанные темы:
- [[8. Когда использовать Builder]] — порождающий паттерн для сложных объектов
- [[9. Что такое Prototype pattern]] — порождающий паттерн копирования
- [[1. Что такое паттерны проектирования]] — общее введение
- [[2. Какие категории паттерны существуют]] — Creational паттерны
- [[10. Когда использовать Strategy]] — поведенческий паттерн, альтернатива через композицию