Как паттерн Абстрактная Фабрика определяет, какую конкретную реализацию объектов создавать

«Как паттерн Абстрактная Фабрика определяет, какую конкретную реализацию объектов создавать» — вопрос из категории Паттерны, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Паттерн Абстрактная Фабрика не "понимает" сам, какую реализацию выбрать. Он делегирует эту ответственность конкретной реализации фабрики, которую выбирает клиентский код на этапе инициализации приложения.

Основная идея — изолировать клиентский код от конкретных классов создаваемых продуктов. Клиент работает только с абстрактными интерфейсами как фабрики, так и продуктов.

Выбор конкретной фабрики происходит один раз, обычно на основе:

  • Конфигурации: Параметр в конфигурационном файле (например, theme = "dark").
  • Окружения: Определение операционной системы или других переменных окружения.
  • Пользовательского ввода: Явный выбор пользователя в настройках.

Пример на Python:

from abc import ABC, abstractmethod

# 1. Абстрактные продукты
class Button(ABC):
    @abstractmethod
    def paint(self): pass

class Checkbox(ABC):
    @abstractmethod
    def paint(self): pass

# 2. Абстрактная фабрика
class GUIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button: pass

    @abstractmethod
    def create_checkbox(self) -> Checkbox: pass

# 3. Конкретные реализации фабрик и продуктов для Windows
class WinButton(Button):
    def paint(self): return "Render a button in Windows style"

class WinCheckbox(Checkbox):
    def paint(self): return "Render a checkbox in Windows style"

class WinFactory(GUIFactory):
    def create_button(self) -> Button: return WinButton()
    def create_checkbox(self) -> Checkbox: return WinCheckbox()

# 4. Конкретные реализации для MacOS
class MacButton(Button):
    def paint(self): return "Render a button in macOS style"

class MacCheckbox(Checkbox):
    def paint(self): return "Render a checkbox in macOS style"

class MacFactory(GUIFactory):
    def create_button(self) -> Button: return MacButton()
    def create_checkbox(self) -> Checkbox: return MacCheckbox()

# 5. Клиентский код выбирает фабрику в зависимости от конфигурации
def get_application(os_type: str):
    if os_type == "windows":
        factory = WinFactory()
    elif os_type == "mac":
        factory = MacFactory()
    else:
        raise ValueError("Unknown OS type")

    # Клиент работает только с абстракциями
    button = factory.create_button()
    print(button.paint())

# Инициализация приложения
CURRENT_OS = "mac"
get_application(CURRENT_OS)
# Вывод: Render a button in macOS style

Таким образом, система остается независимой от того, как именно создаются и компонуются ее объекты. Замена семейства продуктов сводится к замене одной конкретной фабрики.