Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Factory Method и Abstract Factory
factory_abstract_factory

Factory Method и Abstract Factory

Фабричные методы через @classmethod, абстрактные фабрики для семейств объектов. Dependency Injection через фабрики.

Factory Method и Abstract Factory

Фабрики не создают объекты — они скрывают сложность выбора.


#Factory Method: инкапсуляция создания объектов

Factory Method определяет интерфейс для создания объекта, но позволяет подклассам или параметрам решать, какой именно класс instantiate.

#Проблема: слишком много if/else

# ❌ ПЛОХО: Клиент знает о конкретных классах class PaymentProcessor: def process(self, payment_type: str, amount: float): if payment_type == "card": payment = CardPayment(amount) elif payment_type == "paypal": payment = PayPalPayment(amount) elif payment_type == "crypto": payment = CryptoPayment(amount) else: raise ValueError(f"Unknown type: {payment_type}") payment.pay() # Проблема: # 1. Нарушен Open-Closed Principle — добавление типа требует изменения кода # 2. Клиент зависит от конкретных классов # 3. Сложно тестировать — нельзя подменить создание объектов

#Решение 1: Factory Method через @classmethod

# ✅ ХОРОШО: Фабричный метод from abc import ABC, abstractmethod class Payment(ABC): def __init__(self, amount: float): self.amount = amount @abstractmethod def pay(self): pass class CardPayment(Payment): def pay(self): print(f"Processing card payment: ${self.amount}") class PayPalPayment(Payment): def pay(self): print(f"Processing PayPal payment: ${self.amount}") class CryptoPayment(Payment): def pay(self): print(f"Processing crypto payment: ${self.amount}") class PaymentFactory: @classmethod def create(cls, payment_type: str, amount: float) -> Payment: factories = { "card": CardPayment, "paypal": PayPalPayment, "crypto": CryptoPayment, } if payment_type not in factories: raise ValueError(f"Unknown type: {payment_type}") return factories[payment_type](amount) # Использование payment = PaymentFactory.create("card", 100.0) payment.pay() # Processing card payment: $100.0

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

  • Добавление нового типа — просто добавьте запись в словарь
  • Клиент работает с абстракцией Payment
  • Легко тестировать через подмену фабрики

#Решение 2: Factory Method через init_subclass

# ✅ Pythonic: Автоматическая регистрация подклассов class Payment(ABC): _registry = {} def __init_subclass__(cls, payment_type: str = None, **kwargs): super().__init_subclass__(**kwargs) if payment_type: cls._registry[payment_type] = cls def __init__(self, amount: float): self.amount = amount @abstractmethod def pay(self): pass @classmethod def create(cls, payment_type: str, amount: float) -> "Payment": if payment_type not in cls._registry: raise ValueError(f"Unknown type: {payment_type}") return cls._registry[payment_type](amount) # Подклассы автоматически регистрируются @Payment.register class CardPayment(Payment, payment_type="card"): def pay(self): print(f"Card: ${self.amount}") class PayPalPayment(Payment, payment_type="paypal"): def pay(self): print(f"PayPal: ${self.amount}") # Использование payment = Payment.create("paypal", 50.0)

#Решение 3: Factory через отдельный класс

from typing import Protocol, Type class Payment(Protocol): amount: float def pay(self) -> None: ... class PaymentFactory(Protocol): def create(self, amount: float) -> Payment: ... class CardPayment: def __init__(self, amount: float): self.amount = amount def pay(self): print(f"Card: ${self.amount}") class CardPaymentFactory: def create(self, amount: float) -> CardPayment: return CardPayment(amount) # Dependency Injection через фабрику class PaymentProcessor: def __init__(self, factory: PaymentFactory): self.factory = factory def process(self, amount: float): payment = self.factory.create(amount) payment.pay() # В production processor = PaymentProcessor(CardPaymentFactory()) # В тестах class MockPaymentFactory: def create(self, amount: float): class MockPayment: def pay(self): pass return MockPayment() processor = PaymentProcessor(MockPaymentFactory())

#Abstract Factory: семейства связанных объектов

Abstract Factory предоставляет интерфейс для создания семейств связанных или зависимых объектов без привязки к конкретным классам.

#Проблема: согласованность объектов

# ❌ ПЛОХО: Клиент может смешать несовместимые объекты class Button: def render(self): pass class Checkbox: def render(self): pass class WindowsButton(Button): def render(self): print("Render Windows button") class MacButton(Button): def render(self): print("Render Mac button") class WindowsCheckbox(Checkbox): def render(self): print("Render Windows checkbox") class MacCheckbox(Checkbox): def render(self): print("Render Mac checkbox") # Клиент может создать несовместимую смесь: button = WindowsButton() checkbox = MacCheckbox() # ❌ Разные OS!

#Решение: Abstract Factory

# ✅ ХОРОШО: Фабрика создаёт совместимые объекты from abc import ABC, abstractmethod class GUIFactory(ABC): @abstractmethod def create_button(self) -> Button: pass @abstractmethod def create_checkbox(self) -> Checkbox: pass class WindowsFactory(GUIFactory): def create_button(self) -> Button: return WindowsButton() def create_checkbox(self) -> Checkbox: return WindowsCheckbox() class MacFactory(GUIFactory): def create_button(self) -> Button: return MacButton() def create_checkbox(self) -> Checkbox: return MacCheckbox() class Application: def __init__(self, factory: GUIFactory): self.button = factory.create_button() self.checkbox = factory.create_checkbox() def render(self): self.button.render() self.checkbox.render() # Использование app = Application(WindowsFactory()) # Или MacFactory() app.render()

Гарантия: Все созданные объекты принадлежат одному семейству.


#Factory в реальном коде

#Парсеры конфигурации

from abc import ABC, abstractmethod from typing import Protocol import json import yaml class ConfigParser(ABC): @abstractmethod def parse(self, content: str) -> dict: pass class JSONParser(ConfigParser): def parse(self, content: str) -> dict: return json.loads(content) class YAMLParser(ConfigParser): def parse(self, content: str) -> dict: return yaml.safe_load(content) class ConfigParserFactory: _parsers = { ".json": JSONParser, ".yaml": YAMLParser, ".yml": YAMLParser, } @classmethod def get_parser(cls, filename: str) -> ConfigParser: from pathlib import Path ext = Path(filename).suffix.lower() if ext not in cls._parsers: raise ValueError(f"Unsupported format: {ext}") return cls._parsers[ext]() # Использование parser = ConfigParserFactory.get_parser("config.yaml") config = parser.parse("key: value")

#Фабрика для тестирования

class DatabaseConnection: def __init__(self, url: str): self.url = url self._connected = False def connect(self): print(f"Connecting to {self.url}") self._connected = True def query(self, sql: str): if not self._connected: raise RuntimeError("Not connected") # ... реальный запрос ... return [] class DatabaseFactory: @staticmethod def create(url: str) -> DatabaseConnection: return DatabaseConnection(url) class MockDatabaseConnection: def __init__(self, url: str): self.url = url self._data = {"users": []} def connect(self): pass # No-op для тестов def query(self, sql: str): return self._data.get("users", []) class MockDatabaseFactory: @staticmethod def create(url: str) -> MockDatabaseConnection: return MockDatabaseConnection(url) # В production db = DatabaseFactory.create("postgresql://localhost") # В тестах db = MockDatabaseFactory.create("test://")

#Dependency Injection через Factory

from typing import Callable, TypeVar T = TypeVar('T') class Container: """Простой DI-контейнер через фабрики""" def __init__(self): self._services = {} self._instances = {} def register(self, name: str, factory: Callable[[], T], singleton: bool = True): self._services[name] = (factory, singleton) def get(self, name: str) -> T: if name not in self._services: raise KeyError(f"Service not found: {name}") factory, singleton = self._services[name] if singleton: if name not in self._instances: self._instances[name] = factory() return self._instances[name] return factory() # Использование container = Container() container.register("logger", lambda: Logger()) container.register("database", lambda: DatabaseConnection("postgresql://...")) container.register( "user_service", lambda: UserService(container.get("logger"), container.get("database")), singleton=True ) # Получение сервисов user_service = container.get("user_service")

#Когда НЕ использовать Factory

#Избыточная абстракция

# ❌ ПЛОХО: Factory без необходимости class UserFactory: @staticmethod def create(name: str, email: str) -> User: return User(name=name, email=email) # Зачем это, если можно просто: user = User(name="Alice", email="alice@example.com")

Правило: Используйте Factory, когда:

  1. Есть несколько подклассов и выбор зависит от параметров
  2. Нужно скрыть сложность создания объекта
  3. Требуется гибкость для тестирования (DI)

#Сравнение Factory Method vs Abstract Factory

АспектFactory MethodAbstract Factory
НазначениеСоздание одного объектаСоздание семейства объектов
СтруктураОдин методНесколько методов
ГибкостьПодклассы решают, что создаватьФабрика определяет семейство
СложностьПрощеСложнее

#Что дальше?

Изучите тему Builder и Fluent Interface для пошагового создания сложных объектов.

Далее: Builder и Fluent Interface