팩토리(Factory) 패턴 : 심화
Method Factory와 Abstract Factory 패턴의 차이점과 구현 방법

팩토리 패턴의 종류
앞서 본 패턴은 가장 기본적인 구조로 SimpleFactory
패턴이라고 합니다. 이번에는 나머지 두 가지 패턴에 대해서 알아보도록 하겠습니다.
- MethodFactory
- AbstractFactory
메서드 팩토리 패턴(Method Factory)
객체 생성 로직을 서브클래스로 캡슐화하여 유연성과 확장성을 제공합니다. 객체 생성을 위한 인터페이스를 정의하지만, 어떤 클래스의 인스턴스를 생성할지는 서브클래스가 결정하도록 합니다.
상황
피자 가게가 너무너무 잘됐습니다. 각 도시마다 지점을 낸다고 생각해 보겠습니다. 그리고 각 지점은 같은 메뉴라도, 해당 지역의 특색이 감미되게 공장에서 재료를 가공해서 배달합니다.
코드
우선은 각 지역의 피자를 만들어 두겠습니다. 그리고 피자 분기문에 사용할 enum
타입을 하나 만들어 두겠습니다.
from abc import ABC
class Pizza(ABC):
name = "피자"
class NYStyleCheesePizza(Pizza):
name = "뉴욕 치즈 핏자"
class ChicagoStyleCheesePizza(Pizza):
name = "시카고 치즈 핏자"
from enum import StrEnum
class PizzaType(StrEnum):
CHEESE = "cheese"
이제 가장 중요한 팩토리 클래스를 작성하겠습니다. 이 패턴의 가장 핵심은 인터페이스에서 객체 생성 메서드를 선언하고, 하위 클래스가 이 메서드를 구현하는 것입니다.
즉 여러개의 팩토리 클래스가 만들어 질 수 있고, 각 팩토리 클래스마다 원하는 구현을 추가 할 수 있다는 장점이 있습니다.
from abc import ABC, abstractmethod
from typing import override
class MethodFactory(ABC):
@abstractmethod
def make_pizza(self, pizza_type):
pass
class NYPizzaFactory(MethodFactory):
@override
def make_pizza(self, pizza_type):
match pizza_type:
case PizzaType.CHEESE:
return NYStyleCheesePizza()
class ChicagoPizzaFactory(MethodFactory):
@override
def make_pizza(self, pizza_type):
self.cut_rectangle()
match pizza_type:
case PizzaType.CHEESE:
return ChicagoStyleCheesePizza()
def cut_rectangle(self):
print("시카고 피자는 모양이 네모납니다.")
이제 팩토리 객체를 사용하는 클라이언트는 객체 주입을 통해 각 상황에 알맞은 팩토리 객체를 받아서 사용하면 됩니다. 뉴욕 지점을 만들고 싶다면 뉴욕 팩토리를 받아서 피자를 만들고, 시카고 지점을 만들고 싶으면 시카고 팩토리를 넣어주면 되는거죠.
class PizzaStore:
def __init__(self, factory: MethodFactory) -> None:
self.factory = factory
def order(self, pizza_type: PizzaType) -> Pizza:
pizza = self.factory.make_pizza(pizza_type)
return pizza
if __name__ == "__main__":
ny_factor = NYPizzaFactory()
ny_store = PizzaStore(ny_factor)
ny_store.order(PizzaType.CHEESE)
ch_factory = ChicagoPizzaFactory()
ch_store = PizzaStore(ch_factory)
ch_store.order(PizzaType.CHEESE)
같은 치즈 피자라도 각 팩토리를 통해서 다양한 특색을 갖게 만들 수 있습니다.
뉴욕 치즈 핏자를 주문하셨습니다.
시카고 피자는 모양이 네모납니다.
시카고 치즈 핏자를 주문하셨습니다.
클래스 다이어그램 : MethodFactory
%%{ init: { 'theme': 'base', 'themeVariables': { 'primaryColor': '#2a3844', 'lineColor': '#fff', 'primaryTextColor': '#fff', 'tertiaryColor': '#fff' } } }%% classDiagram class PizzaStore{ -MethodFactory factory +order() Pizza } class MethodFactory { <<interface>> +makePizza(type) Pizza* } class NYPizzaFactory { +makePizza(type) Pizza } class ChicagoPizzaFactory { +makePizza(type) Pizza +cut_rectangle() } PizzaStore o-- MethodFactory MethodFactory <|.. NYPizzaFactory MethodFactory <|.. ChicagoPizzaFactory class Pizza { <<abstract>> str name } class NYStyleCheesePizza { str name } class ChicagoStyleCheesePizza { str name } NYStyleCheesePizza --|> Pizza ChicagoStyleCheesePizza --|> Pizza NYPizzaFactory *-- NYStyleCheesePizza ChicagoPizzaFactory *-- ChicagoStyleCheesePizza
추상 팩토리(Abstract Factory)
추상 팩토리 패턴은 관련된 객체들의 군집을 생성하기 위한 인터페이스를 제공하는 생성 패턴입니다. 서로 관련되거나 의존적인 객체들의 모아두고 생성하는 특징이 있습니다.
상황
피자에 필요한 각 재료를 만드는 공장을 한번 생각해 봅시다. 공장은 피자에 필요한 도우와 치즈를 만듭니다. 각 공장은 서로 다른 도우와 치즈를 만들고 있습니다.
- 뉴욕 공장은
ThinCrustDough
와ReggianoCheese
를 만듭니다. - 시카고 공장은
ThickCrustDough
와MozzarellaCheese
를 만듭니다.
각 지점에서는 공장에서 받은 재료들로 피자를 만들면 됩니다.
코드
우선은 각각의 재료를 만들겠습니다. 도우와 치즈를 추상적인 형태로 만들고 이를 구체적인 재료 클래스에서 구현하면 됩니다.
- Dough :
ThinCrustDough
/ThickCrustDough
- Cheese :
ReggianoCheese
/MozzarellaCheese
from abc import ABC, abstractmethod
from typing import override
class Dough(ABC):
@abstractmethod
def __str__(self) -> str:
pass
class ThinCrustDough(Dough):
@override
def __str__(self) -> str:
return "ThinCrustDough"
class ThickCrustDough(Dough):
@override
def __str__(self) -> str:
return "ThickCrustDough"
class Cheese(ABC):
@abstractmethod
def __str__(self) -> str:
pass
class ReggianoCheese(Cheese):
def __str__(self) -> str:
return "ReggianoCheese"
class MozzarellaCheese(Cheese):
def __str__(self) -> str:
return "MozzarellaCheese"
팩토리 인터페이스를 만들어야 하는데, 각 재료별 객체를 생성하는 메서드 선언합니다. 카테고리별로 추상 메서드를 선언해둔다고 생각하면 됩니다. 실제 팩토리에서 서로 관련되거나 의존적인 객체들을 카테고리별로 구현해주면 됩니다.
여기서는 공장에 재료별로 하나씩 선택해서 묶어보도록 하겠습니다.
- 뉴욕 공장 :
ThinCrustDough
+ReggianoCheese
- 시카고 공장 :
ThickCrustDough
+MozzarellaCheese
from abc import ABC, abstractmethod
class AbstractFactory(ABC):
name = "공장"
@abstractmethod
def make_dough(self) -> Dough:
pass
@abstractmethod
def make_cheese(self) -> Cheese:
pass
class NYPizzaFactory(AbstractFactory):
name = "뉴욕 공장"
def make_dough(self) -> Dough:
return ThinCrustDough()
def make_cheese(self) -> Cheese:
return ReggianoCheese()
class ChicagoPizzaFactory(AbstractFactory):
name = "시카고 공장"
def make_dough(self) -> Dough:
return ThickCrustDough()
def make_cheese(self) -> Cheese:
return MozzarellaCheese()
이제 클라이언트에서 각 팩토리를 통해 연관이 있는 재료들을 받을 수 있습니다. 뉴욕 공장에서 받은 치즈와 도우로 뉴욕 스타일의 피자를! 시카고 공장에서 받은 치즈와 도우로 시카고 스타일의 피자를 만들수 있게 됩니다!
class PizzaStore:
def __init__(self, factory: AbstractFactory):
self.factory = factory
def order(self):
cheese = self.factory.make_cheese()
dough = self.factory.make_dough()
return self.make_pizza(cheese, dough)
def make_pizza(self, cheese, dough):
print(f"{self.factory.name}에서 받은")
print(f"{cheese} 치즈와 {dough} 도우를 사용하여 피자를 만듭니다.")
if __name__ = "__main__":
ny_factor = NYPizzaFactory()
ny_store = PizzaStore(ny_factor)
ny_store.order()
ch_factory = ChicagoPizzaFactory()
ch_store = PizzaStore(ch_factory)
ch_store.order()
뉴욕 공장에서 받은
ReggianoCheese 치즈와 ThinCrustDough 도우를 사용하여 피자를 만듭니다.
시카고 공장에서 받은
MozzarellaCheese 치즈와 ThickCrustDough 도우를 사용하여 피자를 만듭니다.
클래스 다이어그램 : Abstract Factory
%%{ init: { 'theme': 'base', 'themeVariables': { 'primaryColor': '#2a3844', 'lineColor': '#fff', 'primaryTextColor': '#fff', 'tertiaryColor': '#fff' } } }%% classDiagram direction RL class Dough { <<abstract>> } class Cheese { <<abstract>> } class ThinCrustDough { } class ThickCrustDough { } ThinCrustDough --|> Dough ThickCrustDough --|> Dough class AbstractFactory { <<interface>> +makeDough() Dough* +makeCheese() Cheese* } class NYPizzaFactory { +makeDough() Dough +makeCheese() Cheese } class ChicagoPizzaFactory { +makeDough() Dough +makeCheese() Cheese } class ReggianoCheese { } class MozzarellaCheese { } class PizzaStore{ -AbstractFactory factory +order() Pizza -make_pizza(cheese, dough) Pizza } ReggianoCheese --|> Cheese MozzarellaCheese --|> Cheese NYPizzaFactory *-- ReggianoCheese NYPizzaFactory *-- ThinCrustDough ChicagoPizzaFactory *-- MozzarellaCheese ChicagoPizzaFactory *-- ThickCrustDough AbstractFactory <|.. NYPizzaFactory AbstractFactory <|.. ChicagoPizzaFactory AbstractFactory --o PizzaStore
마무리
팩토리 메서드 패턴
- 단일 메서드를 사용하여 하나의 제품을 생성
- 일반적으로 하나의 추상 메서드를 포함하는 인터페이스나 추상 클래스를 정의
- 각 구체적인 팩토리 클래스는 이 메서드를 구현하여 특정 객체를 생성
추상 팩토리 패턴
- 여러 메서드를 사용하여 관련된 여러 제품을 생성
- 여러 개의 추상 메서드를 포함하는 인터페이스나 추상 클래스를 정의
- 각 구체적인 팩토리 클래스는 이 메서드들을 구현하여 관련된 여러 객체를 생성
추상 팩토리는 실제로 여러 개의 “팩토리 메서드”를 포함하고 있는 형태라고 볼 수 있습니다.