Что такое паттерн Factory Method (Фабричный метод)?

Ответ

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

Структура и участники:

  1. Product — интерфейс создаваемых объектов.
  2. ConcreteProduct — конкретные реализации Product.
  3. Creator — абстрактный класс, объявляющий фабричный метод createProduct(). Он может содержать бизнес-логику, работающую с продуктами.
  4. ConcreteCreator — переопределяет фабричный метод, чтобы возвращать конкретный экземпляр ConcreteProduct.

Пример на Java:

// 1. Product (Интерфейс продукта)
interface Button {
    void render();
    void onClick();
}

// 2. Concrete Products (Конкретные продукты)
class WindowsButton implements Button {
    public void render() { System.out.println("Render Windows-style button"); }
    public void onClick() { System.out.println("Windows button clicked"); }
}

class HtmlButton implements Button {
    public void render() { System.out.println("Render HTML button"); }
    public void onClick() { System.out.println("HTML button clicked"); }
}

// 3. Creator (Создатель)
abstract class Dialog {
    // ФАБРИЧНЫЙ МЕТОД (может быть абстрактным или иметь реализацию по умолчанию)
    abstract Button createButton();

    // Бизнес-логика, не зависящая от конкретного типа кнопки
    public void renderWindow() {
        Button okButton = createButton(); // Используем фабричный метод
        okButton.render();
        okButton.onClick();
    }
}

// 4. Concrete Creators (Конкретные создатели)
class WindowsDialog extends Dialog {
    @Override
    Button createButton() {
        return new WindowsButton(); // Создаем конкретную кнопку для Windows
    }
}

class WebDialog extends Dialog {
    @Override
    Button createButton() {
        return new HtmlButton(); // Создаем конкретную кнопку для Web
    }
}

// Клиентский код
public class Application {
    private Dialog dialog;

    void initialize(String os) {
        if (os.equals("Windows")) {
            dialog = new WindowsDialog();
        } else {
            dialog = new WebDialog();
        }
    }

    void main() {
        initialize(System.getProperty("os.name"));
        dialog.renderWindow(); // Будет создана и отрисована нужная кнопка
    }
}

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

  • Избавляет от жесткой привязки к конкретным классам продуктов.
  • Соблюдает принцип открытости/закрытости (OCP) — для добавления нового типа продукта нужно создать новый подкласс Creator, не меняя существующий код.
  • Переносит код создания продуктов в одно место, упрощая поддержку.
  • Упрощает юнит-тестирование, так как можно подменить фабричный метод.

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

  • Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать ваш код.
  • Когда вы хотите предоставить возможность расширения вашей библиотеки или фреймворка.
  • Когда нужно дать подклассам возможность выбирать создаваемый объект (как в примере с кнопками для разных ОС).

Ответ 18+ 🔞

Давай разберем эту вашу фабрику, блядь. Сидишь ты такой, пишешь код, и тут понимаешь — а я нихуя не знаю, какую кнопку создавать! WindowsButton или HtmlButton? А если завтра понадобится кнопка для холодильника, блядь? Пиздец, а не архитектура.

Вот тут-то и выплывает наш спаситель — Фабричный Метод. Суть его проста, как три копейки: есть у тебя некий начальник цеха (Creator), который говорит: «Ребята, я знаю, как работать с кнопками, но вот делать их — это не моя ебальная проблема. Пусть каждый цех (ConcreteCreator) делает свою».

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

  1. Продукт (Product) — это типа общий стандарт, контракт. Все кнопки должны уметь render() и onClick(). Ну типа, любая кнопка, хоть деревянная, хоть нарисованная, должна отрисовываться и на неё можно тыкнуть. Интерфейс Button — это и есть тот самый указ свыше.

  2. Конкретная хрень (ConcreteProduct) — это уже реализация. Вот WindowsButton — толстая, серая, с тенью, нажимается со звуком «клик». А HtmlButton — это просто див с бордером, который на onclick вызовет какую-нибудь javascript-функцию. Две абсолютно разные сущности, но обе — кнопки, ёпта!

  3. Создатель (Creator) — это главный по тарелочкам, абстрактный класс Dialog. У него есть план, алгоритм, как работать с окном: renderWindow(). В этом плане сказано: «Чтобы отрендерить окно, мне нужна кнопка «ОК». Где её взять? А я, блядь, не знаю! Пусть мой наследник решает». И он объявляет абстрактный метод createButton(). Это и есть фабричный метод, ебать его в сраку! Вся магия в том, что бизнес-логика (renderWindow) написана в терминах абстрактной кнопки, а не конкретной.

  4. Конкретный создатель (ConcreteCreator) — это уже специализированные цеха. WindowsDialog знает, что он в винде, и его фабричный метод createButton() возвращает new WindowsButton(). WebDialog — такой же патриот, но веба, и возвращает new HtmlButton(). Каждый сам решает, какую конкретную хрень производить.

И что в итоге? Клиентский код (Application) смотрит на операционку и говорит: «Ага, Windows. Значит, нужен WindowsDialog». Дальше он просто вызывает dialog.renderWindow() и похуй, что там внутри. А внутри WindowsDialog.renderWindow() вызовет свой фабричный метод, который создаст правильную кнопку. Всё, приехали.

Преимущества, блядь:

  • Избавляет от жесткой привязки. Твой высокоуровневый код (Dialog) не забит хуйней вроде new WindowsButton(). Он работает с абстракцией.
  • Принцип открытости/закрытости (OCP) наше всё. Захотел кнопку для Android? Не лезешь в старый код с паяльником и криками «мама!». Просто создаёшь новый класс AndroidDialog и в нём переопределяешь createButton(), чтобы он возвращал new AndroidButton(). Старый код даже не чихнет.
  • Всё в одном месте. Если логика создания WindowsButton вдруг усложнится (ну там, нужно десять параметров в конструктор передать, блядь), ты меняешь её только в одном месте — в фабричном методе WindowsDialog. А не бегаешь по всему проекту, как угорелый.
  • Тестирование. Хочешь протестировать Dialog? Подсуни ему в фабричный метод заглушку (MockButton), а не реальную кнопку. Красота, ёпта!

Когда это всё городить?

  • Когда твой код должен работать с разными типами объектов, но ты на этапе написания этого кода нихуя не знаешь, с какими именно.
  • Когда пишешь библиотеку или фреймворк и хочешь, чтобы другие распиздяи могли легко подключать свои собственные штуки, не переписывая твой код.
  • Ну и как в примере — когда логика создания объекта зависит от контекста (ОС, тип UI, фаза луны, блядь).

Короче, паттерн как бы говорит: «Не спрашивай, откуда взять объект — просто запроси его, а система сама разберётся, какого хуя тебе подсунуть». Удобная штука, если не перепираться с ней.