Абстрактная фабрика на Python
Абстрактная фабрика — это порождающий паттерн проектирования, который решает проблему создания целых семейств связанных продуктов, без указания конкретных классов продуктов.
Абстрактная фабрика задаёт интерфейс создания всех доступных типов продуктов, а каждая конкретная реализация фабрики порождает продукты одной из вариаций. Клиентский код вызывает методы фабрики для получения продуктов, вместо самостоятельного создания с помощью оператора new
. При этом фабрика сама следит за тем, чтобы создать продукт нужной вариации.
Сложность:
Популярность:
Применимость: Паттерн можно часто встретить в Python-коде, особенно там, где требуется создание семейств продуктов (например, внутри фреймворков).
Признаки применения паттерна: Паттерн можно определить по методам, возвращающим фабрику, которая, в свою очередь, используется для создания конкретных продуктов, возвращая их через абстрактные типы или интерфейсы.
Концептуальный пример
Этот пример показывает структуру паттерна Абстрактная фабрика , а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.py: Пример структуры паттерна
from __future__ import annotations
from abc import ABC, abstractmethod
class AbstractFactory(ABC):
"""
Интерфейс Абстрактной Фабрики объявляет набор методов, которые возвращают
различные абстрактные продукты. Эти продукты называются семейством и связаны
темой или концепцией высокого уровня. Продукты одного семейства обычно могут
взаимодействовать между собой. Семейство продуктов может иметь несколько
вариаций, но продукты одной вариации несовместимы с продуктами другой.
"""
@abstractmethod
def create_product_a(self) -> AbstractProductA:
pass
@abstractmethod
def create_product_b(self) -> AbstractProductB:
pass
class ConcreteFactory1(AbstractFactory):
"""
Конкретная Фабрика производит семейство продуктов одной вариации. Фабрика
гарантирует совместимость полученных продуктов. Обратите внимание, что
сигнатуры методов Конкретной Фабрики возвращают абстрактный продукт, в то
время как внутри метода создается экземпляр конкретного продукта.
"""
def create_product_a(self) -> AbstractProductA:
return ConcreteProductA1()
def create_product_b(self) -> AbstractProductB:
return ConcreteProductB1()
class ConcreteFactory2(AbstractFactory):
"""
Каждая Конкретная Фабрика имеет соответствующую вариацию продукта.
"""
def create_product_a(self) -> AbstractProductA:
return ConcreteProductA2()
def create_product_b(self) -> AbstractProductB:
return ConcreteProductB2()
class AbstractProductA(ABC):
"""
Каждый отдельный продукт семейства продуктов должен иметь базовый интерфейс.
Все вариации продукта должны реализовывать этот интерфейс.
"""
@abstractmethod
def useful_function_a(self) -> str:
pass
"""
Конкретные продукты создаются соответствующими Конкретными Фабриками.
"""
class ConcreteProductA1(AbstractProductA):
def useful_function_a(self) -> str:
return "The result of the product A1."
class ConcreteProductA2(AbstractProductA):
def useful_function_a(self) -> str:
return "The result of the product A2."
class AbstractProductB(ABC):
"""
Базовый интерфейс другого продукта. Все продукты могут взаимодействовать
друг с другом, но правильное взаимодействие возможно только между продуктами
одной и той же конкретной вариации.
"""
@abstractmethod
def useful_function_b(self) -> None:
"""
Продукт B способен работать самостоятельно...
"""
pass
@abstractmethod
def another_useful_function_b(self, collaborator: AbstractProductA) -> None:
"""
...а также взаимодействовать с Продуктами A той же вариации.
Абстрактная Фабрика гарантирует, что все продукты, которые она создает,
имеют одинаковую вариацию и, следовательно, совместимы.
"""
pass
"""
Конкретные Продукты создаются соответствующими Конкретными Фабриками.
"""
class ConcreteProductB1(AbstractProductB):
def useful_function_b(self) -> str:
return "The result of the product B1."
"""
Продукт B1 может корректно работать только с Продуктом A1. Тем не менее, он
принимает любой экземпляр Абстрактного Продукта А в качестве аргумента.
"""
def another_useful_function_b(self, collaborator: AbstractProductA) -> str:
result = collaborator.useful_function_a()
return f"The result of the B1 collaborating with the ({result})"
class ConcreteProductB2(AbstractProductB):
def useful_function_b(self) -> str:
return "The result of the product B2."
def another_useful_function_b(self, collaborator: AbstractProductA):
"""
Продукт B2 может корректно работать только с Продуктом A2. Тем не менее,
он принимает любой экземпляр Абстрактного Продукта А в качестве
аргумента.
"""
result = collaborator.useful_function_a()
return f"The result of the B2 collaborating with the ({result})"
def client_code(factory: AbstractFactory) -> None:
"""
Клиентский код работает с фабриками и продуктами только через абстрактные
типы: Абстрактная Фабрика и Абстрактный Продукт. Это позволяет передавать
любой подкласс фабрики или продукта клиентскому коду, не нарушая его.
"""
product_a = factory.create_product_a()
product_b = factory.create_product_b()
print(f"{product_b.useful_function_b()}")
print(f"{product_b.another_useful_function_b(product_a)}", end="")
if __name__ == "__main__":
"""
Клиентский код может работать с любым конкретным классом фабрики.
"""
print("Client: Testing client code with the first factory type:")
client_code(ConcreteFactory1())
print("\n")
print("Client: Testing the same client code with the second factory type:")
client_code(ConcreteFactory2())
Output.txt: Результат выполнения
Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)
Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)