Ответ
Паттерн Абстрактная Фабрика не "понимает" сам, какую реализацию выбрать. Он делегирует эту ответственность конкретной реализации фабрики, которую выбирает клиентский код на этапе инициализации приложения.
Основная идея — изолировать клиентский код от конкретных классов создаваемых продуктов. Клиент работает только с абстрактными интерфейсами как фабрики, так и продуктов.
Выбор конкретной фабрики происходит один раз, обычно на основе:
- Конфигурации: Параметр в конфигурационном файле (например,
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
Таким образом, система остается независимой от того, как именно создаются и компонуются ее объекты. Замена семейства продуктов сводится к замене одной конкретной фабрики.