Что такое паттерн ‘Фабрика’ и когда его следует применять

Ответ

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

Основные цели и преимущества:

  • Инкапсуляция логики создания: Клиентскому коду не нужно знать о конкретных классах и сложностях их создания. Он просто запрашивает нужный объект у фабрики.
  • Снижение связанности (Decoupling): Клиентский код зависит от абстрактного интерфейса продукта и фабрики, а не от конкретных реализаций. Это упрощает замену или добавление новых типов продуктов.
  • Соблюдение принципа открытости/закрытости (OCP): Можно вводить новые типы продуктов, не изменяя существующий клиентский код, который использует фабрику.

Пример (Простая Фабрика на Python):

from abc import ABC, abstractmethod

# Абстрактный продукт
class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount: float):
        pass

# Конкретные продукты
class CreditCard(PaymentMethod):
    def pay(self, amount: float):
        print(f"Оплачено {amount} кредитной картой.")

class PayPal(PaymentMethod):
    def pay(self, amount: float):
        print(f"Оплачено {amount} через PayPal.")

# Фабрика для создания объектов
class PaymentFactory:
    @staticmethod
    def get_payment_method(method: str) -> PaymentMethod:
        if method == "credit_card":
            return CreditCard()
        elif method == "paypal":
            return PayPal()
        raise ValueError(f"Неизвестный метод оплаты: {method}")

# Клиентский код
payment_method = PaymentFactory.get_payment_method("paypal")
payment_method.pay(100.50)
# Вывод: Оплачено 100.5 через PayPal.

Когда применять:

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

Ответ 18+ 🔞

Да ты посмотри, какой народ-то изобретательный, блядь! Взяли и придумали целый паттерн, чтобы просто объекты создавать, ёпта! Называется Фабрика (Factory). Это ж надо, сука, до такого додуматься!

Ну, короче, смотри. Есть у тебя какая-то хрень, которую нужно создавать. Но создавать её по-разному, в зависимости от условий. И чтобы клиентский код — этот, который пользуется твоей поделкой — не превращался в помойку из if-else на триста строк, его нужно от этой всей кухни отгородить. Вот фабрика и есть такая хитрая жопа, которая берёт на себя весь этот геморрой.

Зачем это, спрашивается, надо, нахуй?

  • Спрятать сраку (инкапсуляция): Тому, кто пользуется, вообще похуй, как там внутри объект склеивается. Он просто говорит: «Эй, фабрика, дай мне вот это». А она уже сама решает, какую конкретно хуйню ему подсунуть и как её собрать.
  • Развязать всё нахуй (снижение связанности): Твой основной код теперь не привязан к конкретным классам. Он общается с абстракцией — интерфейсом продукта и фабрикой. Захотел поменять реализацию — поменял её в одном месте, в фабрике, и всё, пиздец, работает.
  • Не трогай старое, добавляй новое (принцип OCP): Нужен новый тип объекта? Добавил новый класс продукта и дописал пару строк в фабрику. Весь остальной код, который уже работал, можно даже не дышать, он и так сработает.

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

from abc import ABC, abstractmethod

# Это типа общая идея платежа. Все платёжки должны уметь платить.
class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount: float):
        pass

# А вот конкретные ребята, которые умеют платить по-своему.
class CreditCard(PaymentMethod):
    def pay(self, amount: float):
        print(f"Оплачено {amount} кредитной картой. Чик-чирик и готово!")

class PayPal(PaymentMethod):
    def pay(self, amount: float):
        print(f"Оплачено {amount} через PayPal. Пиу-пиу, деньги улетели!")

# А вот она, звезда вечера — наша фабрика, блядь!
# Её задача — рожать нужные объекты, не загружая мозг пользователя.
class PaymentFactory:
    @staticmethod
    def get_payment_method(method: str) -> PaymentMethod:
        if method == "credit_card":
            return CreditCard()
        elif method == "paypal":
            return PayPal()
        # А если пришла какая-то хуйня, которую мы не знаем — сразу в сраку ошибку!
        raise ValueError(f"Ёб твою мать, какой это метод оплаты: {method}?!")

# А теперь смотрим, как этим всем пользоваться. Красота же!
# Хочу платить через PayPal — и не парюсь, как его создавать.
payment_method = PaymentFactory.get_payment_method("paypal")
payment_method.pay(100.50)
# Выведет: Оплачено 100.5 через PayPal. Пиу-пиу, деньги улетели!

Так когда же эту фабрику впихивать, а?

  • Когда сходу нихуя не ясно, объекты какого именно класса придётся создавать. Всё решится в рантайме.
  • Когда хочется отвязать основную логику от всей этой возни с конструкторами и зависимостями при создании объектов.
  • Когда пишешь библиотеку, и нужно дать пользователю удобный способ создавать сложные штуки, не показывая ему свою кухню, а то ещё обосрётся чего.