![추상 팩토리](/images/patterns/cards/abstract-factory-mini.png?id=4c3927c446313a38ce77dfee38111e27)
파이썬으로 작성된 추상 팩토리
추상 팩토리는 생성 디자인 패턴이며, 관련 객체들의 구상 클래스들을 지정하지 않고도 해당 객체들의 제품 패밀리들을 생성할 수 있도록 합니다.
추상 팩토리는 모든 고유한 제품들을 생성하기 위한 인터페이스를 정의하지만 실제 제품 생성은 구상 팩토리 클래스들에 맡깁니다. 또 각 팩토리 유형은 특정 제품군에 해당합니다.
클라이언트 코드는 생성자 호출(new
연산자)로 직접 제품들을 생성하는 대신 팩토리 객체의 생성 메서드들을 호출합니다. 팩토리는 단일 제품 변형에 해당하므로 해당 팩토리의 모든 제품이 호환될 것입니다.
클라이언트 코드는 추상 인터페이스를 통해서만 팩토리 및 제품과 함께 작동하며, 이렇게 하면 클라이언트 코드가 팩토리 객체에 의해 생성된 모든 제품 변형과 함께 작동할 수 있습니다. 새로운 구상 팩토리 클래스를 생성한 후 클라이언트 코드에 전달합니다.
다양한 팩토리 패턴들과 개념들의 차이점을 이해하지 못하셨다면 팩토리 비교를 읽어보세요.
복잡도:
인기도:
사용 예시들: 추상 팩토리 패턴은 파이썬 코드에 자주 사용됩니다. 많은 프레임워크들과 라이브러리들은 이 패턴을 표준 컴포넌트들을 확장 및 사용자 지정하기 위해 사용합니다.
식별: 패턴은 팩토리 객체를 반환하는 메서드들의 존재 여부로 쉽게 인식할 수 있습니다. 그 후 팩토리는 특정 하위 컴포넌트들을 만드는 데 사용됩니다.
개념적인 예시
이 예시는 추상 팩토리 디자인 패턴의 구조를 보여주고 다음 질문에 중점을 둡니다:
- 패턴은 어떤 클래스들로 구성되어 있나요?
- 이 클래스들은 어떤 역할을 하나요?
- 패턴의 요소들은 어떻게 서로 연관되어 있나요?
main.py: 개념적인 예시
from __future__ import annotations
from abc import ABC, abstractmethod
class AbstractFactory(ABC):
"""
The Abstract Factory interface declares a set of methods that return
different abstract products. These products are called a family and are
related by a high-level theme or concept. Products of one family are usually
able to collaborate among themselves. A family of products may have several
variants, but the products of one variant are incompatible with products of
another.
"""
@abstractmethod
def create_product_a(self) -> AbstractProductA:
pass
@abstractmethod
def create_product_b(self) -> AbstractProductB:
pass
class ConcreteFactory1(AbstractFactory):
"""
Concrete Factories produce a family of products that belong to a single
variant. The factory guarantees that resulting products are compatible. Note
that signatures of the Concrete Factory's methods return an abstract
product, while inside the method a concrete product is instantiated.
"""
def create_product_a(self) -> AbstractProductA:
return ConcreteProductA1()
def create_product_b(self) -> AbstractProductB:
return ConcreteProductB1()
class ConcreteFactory2(AbstractFactory):
"""
Each Concrete Factory has a corresponding product variant.
"""
def create_product_a(self) -> AbstractProductA:
return ConcreteProductA2()
def create_product_b(self) -> AbstractProductB:
return ConcreteProductB2()
class AbstractProductA(ABC):
"""
Each distinct product of a product family should have a base interface. All
variants of the product must implement this interface.
"""
@abstractmethod
def useful_function_a(self) -> str:
pass
"""
Concrete Products are created by corresponding Concrete Factories.
"""
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):
"""
Here's the the base interface of another product. All products can interact
with each other, but proper interaction is possible only between products of
the same concrete variant.
"""
@abstractmethod
def useful_function_b(self) -> None:
"""
Product B is able to do its own thing...
"""
pass
@abstractmethod
def another_useful_function_b(self, collaborator: AbstractProductA) -> None:
"""
...but it also can collaborate with the ProductA.
The Abstract Factory makes sure that all products it creates are of the
same variant and thus, compatible.
"""
pass
"""
Concrete Products are created by corresponding Concrete Factories.
"""
class ConcreteProductB1(AbstractProductB):
def useful_function_b(self) -> str:
return "The result of the product B1."
"""
The variant, Product B1, is only able to work correctly with the variant,
Product A1. Nevertheless, it accepts any instance of AbstractProductA as an
argument.
"""
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):
"""
The variant, Product B2, is only able to work correctly with the
variant, Product A2. Nevertheless, it accepts any instance of
AbstractProductA as an argument.
"""
result = collaborator.useful_function_a()
return f"The result of the B2 collaborating with the ({result})"
def client_code(factory: AbstractFactory) -> None:
"""
The client code works with factories and products only through abstract
types: AbstractFactory and AbstractProduct. This lets you pass any factory
or product subclass to the client code without breaking it.
"""
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__":
"""
The client code can work with any concrete factory class.
"""
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.)