정의
the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.
-- 영어 위키백과
Factory method는 부모(상위) 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며. 자식(하위) 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴이기도 하다.
-- 한국어 위키백과
이런식으로 정의만 보면 절대 이해가 안간다.
바로 예시로 들어가보자.
일단 피자가게 사장님이 되었다고 해보자.
1.햄머슈룸 피자
2. 디럭스피자
3. 해산물 피자를 메뉴로 준비하였다.
class HamAndMushroomPizza():
def __init__(self):
self.__price = 8.50
def put_toping(self):
self.topping = "HAM"
class DeluxePizza():
def __init__(self):
self.__price = 10.50
def add_toping(self):
self.topping = "meat"
class SeafoodPizza():
def __init__(self):
self.__price = 11.50
def add_toping(self):
self.topping = "meat"
def add_cheese_crust(self):
self.cheese_crust = True
만약 이렇게 될시에, 소비자가 주문하려고 할때, 각각의 객체생성 방법에 대해 알고 있어야한다.
ham_mushroom_pizza = HamAndMushroomPizza()
delux_pizza = DeluxePizza()
seafood_pizza = SeafoodPizza()
이때, pizzaFactory를 하나 만들어준다면 어떻게 될까?
class PizzaFactory:
"The Factory Class"
@staticmethod
def create_pizza(pizza_type: str):
if pizza_type == 'HAM_MUSHROOM_PIZZA_TYPE':
return HamAndMushroomPizza()
if pizza_type == 'DELUXE_PIZZA_TYPE':
return DeluxePizza()
if pizza_type == 'SEAFOOD_PIZZA_TYPE':
return SeafoodPizza()
return None
pizza = PizzaFactory().create_pizza("HAM_MUSHROOM_PIZZA_TYPE")
pizza.put_toping()
소비자는 피자 팩토리만 알면, 복잡한 객체생성방법에 대해 알 필요가 없게된다.
지금 예시는 간단해서 필요성을 못느낄수도 있지만,
각 피자에 따라 직접 토핑을 집어넣어줘야 피자가 생성됐다면?
팩토리라는 클라스의 필요성을 느낄수 있을 것이다.
이렇게만 바뀌면 객체지향적이라고 할 수 있을까?
SOLID원칙중 open/close원칙에 비추어 확인해보도록 하자.
class PizzaFactory:
"The Factory Class"
@staticmethod
def create_pizza(pizza_type: str):
if pizza_type == 'HAM_MUSHROOM_PIZZA_TYPE':
return HamAndMushroomPizza()
if pizza_type == 'DELUXE_PIZZA_TYPE':
return DeluxePizza()
if pizza_type == 'SEAFOOD_PIZZA_TYPE':
return SeafoodPizza()
return None
pizza = PizzaFactory().create_pizza("SEAFOOD_PIZZA")
pizza.put_toping()
앞선 코드에서 피자종류만, 해산물피자로 바꾸었다.
그런데 에러가 날 것이다.
왜냐하면, SEAFOOD피자에서는 put_toping이 아니라 add_toping이기 때문이다.
이는 즉, 만약 HAM_MUSHROOM_PIZZA를 먼저만들고, SEAFOOD 피자로 확장했다고 했을떄,
확장을 했을때 코드를 수정해야한느 일이 발생한 것이다.
즉, 확장에 열려있고, 수정에 닫혀있어야한다는 open/closed principle에 어긋난다.
그러면 어떻게 해줘야할까?
class Pizza(ABCMeta):
def __init__(self):
self.__price = None
@abstractmethod
def add_toping(self):
pass
class HamAndMushroomPizza(Pizza):
def __init__(self):
self.__price = 8.50
def add_toping(self):
self.topping = "HAM"
class DeluxePizza(Pizza):
def __init__(self):
self.__price = 10.50
def add_toping(self):
self.topping = "MEAT"
class SeafoodPizza(Pizza):
def __init__(self):
self.__price = 11.50
def add_toping(self):
self.topping = "SEAFOOD"
def add_cheese_crust(self):
self.cheese_crust = True
이런식으로 추상화된 클라스를 만들어줘서 통일감있게 강제해줘야한다.
이렇게 추상화된 클라스를 상속받기만 하면, 기존코드 수정 없이, 새로운 피자를 만들어낼 수 있다.(확장)
이는 또한 생성이라는 역할을 factory가 다 맡고 있다는 점에서 단일책임 원칙을 지키고 있다고 볼 수 있다.