Lapa Knowledge Base
Порождающие паттерны

Factory Method

Паттерн Factory Method

Factory Method — это порождающий паттерн проектирования, который определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.

Проблема

Представьте, что вы создаете приложение для логистики. Первая версия может обрабатывать только перевозки грузов на грузовиках, поэтому весь код привязан к классу Truck.

Позже ваше приложение становится популярным, и вы получаете десятки запросов на добавление перевозок по морю. Отлично! Но что насчет кода? Большая часть существующего кода привязана к классу Truck. Добавление класса Shipпотребует изменений во всем коде.

Решение

Паттерн Factory Method предлагает создавать объекты не напрямую, используя оператор new, а через вызов особого фабричного метода. Не волнуйтесь: объекты всё равно будут создаваться через new, но вызываться он будет из фабричного метода.


Структура

Creator
├── factoryMethod(): Product
└── someOperation(): string

ConcreteCreator
└── factoryMethod(): ConcreteProduct

Product
└── operation(): string

ConcreteProduct
└── operation(): string
getInstance(): Singleton

Пример реализации

JavaScript

// Абстрактный класс создателя
class TransportCreator {
    // Фабричный метод
    createTransport() {
        throw new Error('Метод createTransport должен быть переопределен');
    }
    
    // Бизнес-логика
    planDelivery() {
        const transport = this.createTransport();
        return `Планируем доставку с помощью ${transport.deliver()}`;
    }
}

// Конкретный создатель для грузовиков
class TruckCreator extends TransportCreator {
    createTransport() {
        return new Truck();
    }
}

// Конкретный создатель для кораблей
class ShipCreator extends TransportCreator {
    createTransport() {
        return new Ship();
    }
}

// Абстрактный продукт
class Transport {
    deliver() {
        throw new Error('Метод deliver должен быть переопределен');
    }
}

// Конкретные продукты
class Truck extends Transport {
    deliver() {
        return 'грузовика по суше';
    }
}

class Ship extends Transport {
    deliver() {
        return 'корабля по морю';
    }
}

// Использование
function clientCode(creator) {
    console.log(creator.planDelivery());
}

// Создаем доставку грузовиком
const truckCreator = new TruckCreator();
clientCode(truckCreator); // "Планируем доставку с помощью грузовика по суше"

// Создаем доставку кораблем
const shipCreator = new ShipCreator();
clientCode(shipCreator); // "Планируем доставку с помощью корабля по морю"

TypeScript с интерфейсами

// Интерфейс продукта
interface Animal {
    makeSound(): string;
    move(): string;
}

// Интерфейс создателя
interface AnimalFactory {
    createAnimal(): Animal;
}

// Конкретные продукты
class Dog implements Animal {
    makeSound(): string {
        return 'Гав-гав!';
    }
    
    move(): string {
        return 'Бегает на четырех лапах';
    }
}

class Cat implements Animal {
    makeSound(): string {
        return 'Мяу-мяу!';
    }
    
    move(): string {
        return 'Крадется бесшумно';
    }
}

// Конкретные создатели
class DogFactory implements AnimalFactory {
    createAnimal(): Animal {
        return new Dog();
    }
}

class CatFactory implements AnimalFactory {
    createAnimal(): Animal {
        return new Cat();
    }
}

// Использование
function createAndInteract(factory: AnimalFactory) {
    const animal = factory.createAnimal();
    console.log(`Звук: ${animal.makeSound()}`);
    console.log(`Движение: ${animal.move()}`);
}

const dogFactory = new DogFactory();
const catFactory = new CatFactory();

createAndInteract(dogFactory);
createAndInteract(catFactory);

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

Используйте Factory Method, когда:

  • Заранее неизвестно, объекты каких типов нужно создавать
  • Класс делегирует создание объектов подклассам
  • Нужно расширить систему новыми типами продуктов без изменения существующего кода
  • Хотите предоставить библиотеку продуктов и раскрыть только их интерфейсы

Преимущества

  • Избегает тесной связанности между создателем и конкретными продуктами
  • Принцип единственной ответственности: создание продуктов вынесено в одно место
  • Принцип открытости/закрытости: можно добавлять новые типы продуктов без изменения существующего кода
  • Легко добавлять новые типы продуктов

Недостатки

  • Код может стать более сложным из-за введения множества дополнительных классов
  • Нужно создавать иерархию создателей для каждого типа продукта

Отличия от других паттернов

  • Abstract Factory — создает семейства связанных объектов
  • Builder — создает сложные объекты пошагово
  • Prototype — создает объекты путем клонирования

Реальный пример

// Фабрика для создания UI компонентов
class UIComponentFactory {
    createButton(type) {
        switch(type) {
            case 'primary':
                return new PrimaryButton();
            case 'secondary':
                return new SecondaryButton();
            case 'danger':
                return new DangerButton();
            default:
                throw new Error(`Неизвестный тип кнопки: ${type}`);
        }
    }
}

class PrimaryButton {
    render() {
        return '<button class="btn btn-primary">Кнопка</button>';
    }
}

class SecondaryButton {
    render() {
        return '<button class="btn btn-secondary">Кнопка</button>';
    }
}

class DangerButton {
    render() {
        return '<button class="btn btn-danger">Кнопка</button>';
    }
}

// Использование
const factory = new UIComponentFactory();
const primaryBtn = factory.createButton('primary');
console.log(primaryBtn.render());

Factory Method — это основа для создания гибких и расширяемых систем!

Copyright © 2026