Exist Technote
[Python] Abstract Factory Pattern (추상 팩토리 패턴) 본문
참고
예제
- 코드를 직접 분석하고 나서 패턴을 이해하는 것이 옳은 순서라고 생각한다.
첫번째 예제 코드
import random
class PetShop:
def __init__(self, animal_factory=None):
self.pet_factory = animal_factory
def show_pet(self):
pet = self.pet_factory()
print("We have a lovely {}".format(pet))
print("It says {}".format(pet.speak()))
class Dog:
def speak(self):
return "woof"
def __str__(self):
return "Dog"
class Cat:
def speak(self):
return "meow"
def __str__(self):
return "Cat"
def random_animal():
return random.choice([Dog, Cat])()
def main():
cat_shop = PetShop(Cat)
cat_shop.show_pet()
shop = PetShop(random_animal)
for i in range(3):
shop.show_pet()
print("=" * 20)
첫번째 예제 분석
-
PetShop은 생성자에 animal_factory라는 추상 팩토리를 인자로 받는다.
class PetShop: def __init__(self, animal_factory=None): # 추상 팩토리 self.pet_factory = animal_factory # animal_factory 클래스(추상 팩토리)가 def show_pet(self): pet = self.pet_factory() # 호출될 때 객체를 반환하고, 반환된 객체가 print("We have a lovely {}".format(pet)) # __str__ 메서드를 지니기를, print("It says {}".format(pet.speak())) # 또한 speak 메서드를 지니기를 기대한다.
-
팩토리로 사용되는 객체는 총 세 가지이다.
class Dog: def speak(self): return "woof" def __str__(self): return "Dog" class Cat: def speak(self): return "meow" def __str__(self): return "Cat" def random_animal(): return random.choice([Dog, Cat])()
-
추상 팩토리를 변경함으로써, 실제로 사용되는 객체군을 자유롭게 변경할 수 있다.
def main(): cat_shop = PetShop(Cat) cat_shop.show_pet() shop = PetShop(random_animal) for i in range(3): shop.show_pet() print("=" * 20)
두번째 예제 코드
import enum
from abc import *
class Button:
@abstractmethod
def Paint(self):
pass
class WinButton(Button):
def Paint(self):
print ("Render a button in a Windows style")
class OSXButton(Button):
def Paint(self):
print ("Render a button in a Mac OSX style")
class MousePointer:
@abstractmethod
def Paint(self):
pass
class WinMousePointer(MousePointer):
def Paint(self):
print("Render a mousepointer in a Windows style")
class OSXMousePointer(MousePointer):
def Paint(self):
print ("Render a mousepointer in a OSX style")
class GUIFactory:
@abstractmethod
def CreateButton(self):
return Button
@abstractmethod
def CreateMousePointer(self):
return MousePointer
class WinFactory(GUIFactory):
def CreateButton(self):
return WinButton()
def CreateMousePointer(self):
return WinMousePointer()
class OSXFactory(GUIFactory):
def CreateButton(self):
return OSXButton()
def CreateMousePointer(self):
return OSXMousePointer()
class Settings:
@staticmethod
def Default():
return Appearance.WIN
class Appearance(enum.Enum):
WIN = 0
OSX = 1
def main():
apperance = Settings.Default()
if apperance == Appearance.WIN:
factory = WinFactory()
elif apperance == Appearance.OSX:
factory = OSXFactory()
button = factory.CreateButton()
mousePointer = factory.CreateMousePointer()
button.Paint()
mousePointer.Paint()
if __name__ == '__main__':
main()
두번째 예제 분석
-
여러 os에서 사용되는 GUI를 구현하려 한다.
-
GUI는 버튼과 마우스포인터로 구성된다. (더 늘어날 수 있다.)
-
윈도우용 GUI가 있고, 맥용 GUI가 있다. (OS는 더 늘어날 수 있다.)
class Button: @abstractmethod def Paint(self): pass class WinButton(Button): def Paint(self): print ("Render a button in a Windows style") class OSXButton(Button): def Paint(self): print ("Render a button in a Mac OSX style") class MousePointer: @abstractmethod def Paint(self): pass class WinMousePointer(MousePointer): def Paint(self): print("Render a mousepointer in a Windows style") class OSXMousePointer(MousePointer): def Paint(self): print ("Render a mousepointer in a OSX style")
-
-
각 OS에 해당하는 팩토리를 정의한다.
class GUIFactory: @abstractmethod def CreateButton(self): return Button @abstractmethod def CreateMousePointer(self): return MousePointer class WinFactory(GUIFactory): def CreateButton(self): return WinButton() def CreateMousePointer(self): return WinMousePointer() class OSXFactory(GUIFactory): def CreateButton(self): return OSXButton() def CreateMousePointer(self): return OSXMousePointer()
-
enum을 활용하여 추상 팩토리를 간단하게 선택할 수 있도록 설계한다.
class Settings: @staticmethod def Default(): return Appearance.OSX class Appearance(enum.Enum): WIN = 0 OSX = 1
-
사용자는 자유롭게 GUI 그룹을 변경할 수 있다.
def main(): apperance = Settings.Default() if apperance == Appearance.WIN: factory = WinFactory() elif apperance == Appearance.OSX: factory = OSXFactory() button = factory.CreateButton() mousePointer = factory.CreateMousePointer() button.Paint() mousePointer.Paint() if __name__ == '__main__': main()
Abstract factory 패턴이란?
- 사용 목적
- 목적은 동일하지만 개념이 다른 팩토리들을 군으로 묶어 추상 팩토리로 구현하고, 추상 팩토리가 특정 인터페이스를 따른다고 가정하면 객체와 무관하게 시스템을 구현할 수 있게 된다.
- 다양한 환경에서 동작하는 구조, 추상 팩토리가 추가되더라도 확장성 있는 구조를 설계할 수 있게 된다.
- 잡담.
- "객체를 생성하는 팩토리가 동일한 인터페이스를 따르며, 따라서 객체와는 별개로 시스템을 설계할 수 있는 패턴"이라고 정의할 수 있을 것 같다.
- 첫 번째 예제와 두 번재 예제의 차이
- 첫 번째 예제는 추상 팩토리가 객체의 군집화를 표현하지 못한다.
- 두 번째 예제가 해당 군집화를 올바르게 표현하고 있다고 생각한다.
- '파이썬 인 프렉티스' 책에서, 두 번째 예제와 비슷한 코드를 namespace 관련하여 더 파이써닉하게 구성하는 방법을 기술해놓았던 것으로 기억하는데, 어떤 방식인지 다시 살펴봐야 할 듯 하다.
'Design Pattern > Creational' 카테고리의 다른 글
[Python] Object Pool Pattern (오브젝트 풀 패턴) (0) | 2020.10.05 |
---|---|
[Python] Builder Pattern (빌더 패턴) (0) | 2020.10.04 |
[Python] Borg Pattern (보그 패턴) (0) | 2020.10.04 |
[Python] Factory Method Pattern (팩토리 메서드 패턴) (0) | 2020.09.29 |
[Python] Singleton Pattern (싱글톤 패턴) (0) | 2020.09.26 |
Comments