Что такое паттерн проектирования Factory (Фабрика) и его варианты?

Ответ

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

Проблема, которую решает: Прямое создание объектов через new ConcreteClass() жёстко связывает код с конкретными типами, усложняя расширение и тестирование.

Пример на TypeScript (Factory Method):

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

// Конкретные продукты
class Truck implements Transport {
  deliver() { return "Доставка по земле"; }
}
class Ship implements Transport {
  deliver() { return "Доставка по морю"; }
}

// Создатель (Creator) с фабричным методом
abstract class Logistics {
  // Фабричный метод
  abstract createTransport(): Transport;

  planDelivery(): string {
    const transport = this.createTransport();
    return `Планирование: ${transport.deliver()}`;
  }
}

// Конкретные создатели
class RoadLogistics extends Logistics {
  createTransport(): Transport {
    return new Truck();
  }
}

class SeaLogistics extends Logistics {
  createTransport(): Transport {
    return new Ship();
  }
}

// Использование
const roadLogistics = new RoadLogistics();
console.log(roadLogistics.planDelivery()); // Планирование: Доставка по земле

Связанные паттерны:

  1. Простая фабрика (Simple Factory): Отдельный класс/функция, создающая объекты на основе параметра.
    def create_transport(type: str):
       if type == "truck":
           return Truck()
       elif type == "ship":
           return Ship()
       raise ValueError("Unknown transport type")
  2. Абстрактная фабрика (Abstract Factory): Создаёт семейства связанных объектов.

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

  • Соблюдает принцип открытости/закрытости — легко добавлять новые типы продуктов.
  • Упрощает юнит-тестирование через моки и стабы.
  • Убирает дублирование кода инициализации.

Когда использовать: Когда система должна оставаться независимой от способа создания, композиции и представления объектов, или когда создание объекта сложнее простого конструктора.

Ответ 18+ 🔞

Да блядь, ну вот опять эти ваши паттерны! Сидишь такой, пишешь код, а тут бац — и уже нужно создавать объекты, но так, чтобы потом не пришлось переписывать полпрограммы, если начальству взбредёт в голову вместо грузовика корабль захотеть. Ну, типа, классика же!

Фабричный метод — это как такой хитрый папаша, который говорит: «Я не буду сам лепить тебе игрушку, сынок, но скажу, как её нужно лепить». А уж ты сам там из пластилина или из конструктора собирай — мне похуй. Главное, чтобы интерфейс был правильный.

Проблема-то в чём, блядь? А в том, что если ты везде new Truck() втыкаешь, то потом, когда понадобится new Ship(), тебе придётся по всему коду ползать и эти вызовы менять. Это ж пиздец, а не работа! Терпения ноль, ебать.

Смотри, как это на TypeScript выглядит, чтоб ты понимал масштаб трагедии:

// Вот это — что должно получиться в итоге. Общий интерфейс, ёпта.
interface Transport {
  deliver(): string;
}

// Конкретные реализашки. Грузовик — он по земле катается.
class Truck implements Transport {
  deliver() { return "Доставка по земле"; }
}
// А корабль — по воде. Неожиданно, да?
class Ship implements Transport {
  deliver() { return "Доставка по морю"; }
}

// А вот это — самое мясо! Создатель, он же абстрактный начальник цеха.
abstract class Logistics {
  // Фабричный метод, сука! Он абстрактный — то есть, «делай как знаешь, но сделай!»
  abstract createTransport(): Transport;

  // А это уже общая логика, которая использует твой фабричный метод.
  planDelivery(): string {
    const transport = this.createTransport(); // Вот тут магия!
    return `Планирование: ${transport.deliver()}`;
  }
}

// Дочерние классы, которые уже решают, какую хуйню создавать.
class RoadLogistics extends Logistics {
  createTransport(): Transport {
    return new Truck(); // «А я вот грузовик люблю!»
  }
}

class SeaLogistics extends Logistics {
  createTransport(): Transport {
    return new Ship(); // «А мне похуй, я моряк!»
  }
}

// Использование, блядь!
const roadLogistics = new RoadLogistics();
console.log(roadLogistics.planDelivery()); // Выведет про землю, ясень пень.

А есть же ещё, блядь, родственнички!

  1. Простая фабрика (Simple Factory) — это как тётка в отделе кадров: даёшь ей бумажку с типом, а она тебе уже готовый объект выносит. Не патриархат, но работает.

    def create_transport(type: str):
        if type == "truck":
            return Truck()
        elif type == "ship":
            return Ship()
        raise ValueError("Чё за хуйню ты мне подсунул?")
  2. Абстрактная фабрика (Abstract Factory) — это уже целая семейная фабрика, блядь. Там не просто один объект, а сразу набор, чтоб всё гармонично было. Но это уже другая история, с блэкджеком и шлюхами.

И зачем этот цирк, спросишь ты? А затем, ёпта!

  • Принцип открытости/закрытости в действии: хочешь добавить доставку дроном? Просто новый класс создаёшь, расширяешь Logistics — и ни одна старая строчка кода не пострадала. Красота!
  • Тестировать легко, потому что можно в фабричный метод заглушку (mock) воткнуть и проверять логику планирования отдельно от логики создания.
  • Код чище, инициализация в одном месте, а не размазана по всей программе, как дерьмо по стенке.

Когда это всё городить? Да когда создание объекта — это не просто new, а там ещё какие-то проверки, конфигурации, или когда тип объекта должен определяться в рантайме. Или когда просто предчувствуешь, что потом придётся добавлять новые типы — чтобы не охуеть от количества правок.

Вот и весь сказ, блядь. Не так страшен чёрт, как его малюют. Главное — не путать с абстрактной фабрикой, а то совсем пиздец будет.