← Назад к вопросам

Для чего нужен Фабричный метод?

2.0 Middle🔥 171 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Фабричный метод (Factory Method Pattern)

Фабричный метод — это порождающий паттерн проектирования, который предоставляет интерфейс для создания объектов, но позволяет подклассам решать, какой класс инстанцировать.

Основные причины использования

1. Абстракция создания объектов

Вместо прямого использования new, клиент обращается к фабричному методу. Это скрывает детали создания:

// Без паттерна (плохо)
if (type.equals("PDF")) {
    document = new PDFDocument();
} else if (type.equals("WORD")) {
    document = new WordDocument();
}

// С паттерном (хорошо)
Document document = DocumentFactory.createDocument(type);

2. Слабая связанность (Low Coupling)

Клиент зависит от интерфейса, а не от конкретных реализаций:

// Интерфейс
public interface Document {
    void open();
    void save();
}

// Конкретные классы
public class PDFDocument implements Document {
    @Override
    public void open() { /* логика открытия PDF */ }
    
    @Override
    public void save() { /* логика сохранения PDF */ }
}

public class WordDocument implements Document {
    @Override
    public void open() { /* логика открытия WORD */ }
    
    @Override
    public void save() { /* логика сохранения WORD */ }
}

// Фабрика
public class DocumentFactory {
    public static Document createDocument(String type) {
        switch (type.toLowerCase()) {
            case "pdf":
                return new PDFDocument();
            case "word":
                return new WordDocument();
            default:
                throw new IllegalArgumentException("Unknown type: " + type);
        }
    }
}

// Использование
Document doc = DocumentFactory.createDocument("pdf");
doc.open();

3. Централизованное управление инстанцированием

Вся логика создания в одном месте, легче отслеживать и модифицировать:

public class DatabaseFactory {
    private static Map<String, DataSource> pool = new ConcurrentHashMap<>();
    
    public static DataSource createDataSource(String dbType) {
        return pool.computeIfAbsent(dbType, key -> {
            // Сложная логика инициализации БД
            switch (key) {
                case "PostgreSQL":
                    return createPostgresDataSource();
                case "MySQL":
                    return createMysqlDataSource();
                default:
                    throw new IllegalArgumentException("Unknown DB: " + key);
            }
        });
    }
    
    private static DataSource createPostgresDataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
        config.setUsername("user");
        config.setPassword("pass");
        config.setMaximumPoolSize(10);
        return new HikariDataSource(config);
    }
    
    private static DataSource createMysqlDataSource() {
        // аналогично для MySQL
        return new HikariDataSource(config);
    }
}

4. Условное создание с параметрами

Фабрика может выбирать реализацию на основе параметров или конфигурации:

public class PaymentProcessorFactory {
    public static PaymentProcessor create(String provider) {
        switch (provider) {
            case "STRIPE":
                return new StripePaymentProcessor(
                    System.getenv("STRIPE_KEY")
                );
            case "PAYPAL":
                return new PayPalPaymentProcessor(
                    System.getenv("PAYPAL_KEY")
                );
            case "MOCK": // для тестирования
                return new MockPaymentProcessor();
            default:
                throw new IllegalArgumentException("Unknown provider: " + provider);
        }
    }
}

// Использование
PaymentProcessor processor = PaymentProcessorFactory.create(
    System.getenv("PAYMENT_PROVIDER")
);
processor.process(amount);

5. Простое расширение (Open/Closed Principle)

Добавить новый тип можно, не изменяя существующий код (в идеале):

// Если используем рефлексию или конфигурацию
public class ConfigurableFactory {
    private Map<String, Class<?>> registry = new HashMap<>();
    
    public void register(String type, Class<?> cls) {
        registry.put(type, cls);
    }
    
    public Object create(String type) throws Exception {
        Class<?> cls = registry.get(type);
        if (cls == null) throw new IllegalArgumentException("Unknown type");
        return cls.getDeclaredConstructor().newInstance();
    }
}

// Использование
ConfigurableFactory factory = new ConfigurableFactory();
factory.register("pdf", PDFDocument.class);
factory.register("word", WordDocument.class);
factory.register("excel", ExcelDocument.class); // легко добавить новый

6. Spring Framework пример

Spring использует фабричные методы везде:

@Configuration
public class AppConfig {
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost/db");
        return new HikariDataSource(config);
    }
    
    @Bean
    public UserRepository userRepository(DataSource ds) {
        return new JdbcUserRepository(ds);
    }
    
    @Bean
    public UserService userService(UserRepository repo) {
        return new UserService(repo);
    }
}

Когда использовать

  • Класс не может знать точный тип создаваемых объектов
  • Нужна гибкость в выборе реализации
  • Создание сложного объекта требует дополнительной логики
  • Нужно управлять пулом объектов
  • Необходимо логирование/аудит создания объектов

Резюме

Фабричный метод нужен для:

  • Слабой связанности между классами
  • Абстракции процесса создания объектов
  • Централизованного управления инстанцированием
  • Условного выбора реализации
  • Упрощения расширения функционала
Для чего нужен Фабричный метод? | PrepBro