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

Ответ

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

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

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

  • Конфигурации: Параметр в конфигурационном файле (например, 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

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