팩토리란 다른 객체의 클래스를 생성하는 클래스를 말한다. A클래스와 B클래스가 있다. B클래스 안에 A클래스의 객체를 생성하는 코드가 들어 있다면, B클래스를 이용해서 객체를 생성하게되면, A클래스의 객체가 내부적으로 생성이 될 것이다.
이때, B클래스가 팩토리가 된다.
팩토리(B클래스)에는 A클래스의 객체와 관련된 메서드가 있고, 클라이언트로 부터 입력값을 포함 한 인자를 받게되며, 그 인자와 함께 A클래스와 관련된 메서드가 동작하면서 A객체가 생성된다. 팩토리 패턴의 종류에는 심플팩토리, 추상팩토리, 팩토리메서드가 있다.
Factory Pattern
Simple Factory
Abstract Factory
Factory Method
심플팩토리는 팩토리는 객체 생성로직을 숨기며 객체를 생성한다. 모든 팩토리패턴의 포괄적인 개념이라고 할 수 있다.
다른 두개의 패턴에 비해서 간단하며, 두개 패턴을 이해하기 위한 기본개념으로 숙지하도록 하자.
클라이언트가 전달한 인자에 따라서 팩토리안의 클래스를 이용해서 객체를 생성한다.
"""
심플팩토리
"""
from abc import ABCMeta, abstractmethod
# 추상클래스이다.
# Job을 상속받는 모든 클래스는 아래의 do_something를 반드시 오버라이딩해주어야한다.
# 추상클래스를 이용함으로써 오버라이딩에 대해서 강제하고 있다.
class Job(metaclass=ABCMeta):
@abstractmethod
def do_something(self):
pass
class Student(Job):
# 상속받은 추상클래스의 메서드를 오버라이딩한다.
def do_something(self):
print("Let's do the study")
class Worker(Job):
# 상속받은 추상클래스의 메서드를 오버라이딩한다.
def do_something(self):
print("Lets' do the work")
# 팩토리에 해당한다.
# 다른 클래스의 객체를 생성한다.
class SimpleFactory(object):
# 다른클래스의 객체와 관련된 메서드와 그 인자를 포함해서 객체를 생성한다.
def do_it(self, object_type):
return(eval(object_type.capitalize())().do_something())
# job = input("what is your job? Student? Worker?")
# 팩토리로 팩토리안에 들어있는 객체를 생성한다.
# 인자로 student/worker를 받아서 해당하는 클래스의 객체를 생성하게 된다.
# f = SimpleFactory()
# f.do_it(job)
팩토리메서드 패턴은 인스턴스의 작성을 하위클래스(팩토리= DogRobot, BirdRobot)에 위임한다. 생성과 관련된 동일한 메서드는 상위클래스(Robot)에서 정의한다.
여러 상황에 따라 각각 생성될 수 있는 객체에 대한 생성을 하위 클래스에 위임한다. 들어온 키워드(인자)에 해당하는 인스턴스를 반환해주는 역할을 한다.
인스턴스 생성에 대한 제어가 가능하다.
enum이나 map을 이용해서 인자에 따른 다른 결과값을 반환할 수 있다.
객체를 생성하는 인터페이스(Robot)는 미리 정의하되, 객체 생성은 서브 클래스(팩토리= DogRobot, BirdRobot )로 위임하는 패턴이다
"""
팩토리 메서드
"""
from abc import abstractmethod, ABC
# 아래의 로보파트 부분은 팩토리 안에서 객체를 생성하기 위한 코드이다.
# 추상클래스
class RobatPart(ABC):
@abstractmethod
def assemble(self):
pass
class HeadPart(RobatPart):
def assemble(self):
print("assemble head part")
class WingPart(RobatPart):
def assemble(self):
print("assemble wing part")
class LegPart(RobatPart):
def assemble(self):
print("assemble leg part")
class BodyPart(RobatPart):
def assemble(self):
print("assemble body part")
class ArmPart(RobatPart):
def assemble(self):
print("assemble arm part")
# 추상클래스
# 인터페이스 역할을 한다.
# 공통적으로 사용되는 기능을 수행하게끔 강제하는 클래스이다.
class Robot(ABC):
def __init__(self):
self.parts = []
self.create_robot()
@abstractmethod
def create_robot(self):
pass
def add_part(self,part):
self.parts.append(part)
def assemble_part(self):
for part in self.parts:
part.assemble()
# 팩토리
# 실제로 객체를 생성한다.
# 객체가 어떻게 생성될 것인지를 결정한다.
# 아래는 개와 새에 대한 객체만 존재한다.
# 만약 다른 객체를 생성하고자한다면, 추가만 하면된다.
class DogRobot(Robot):
def create_robot(self):
self.add_part(HeadPart())
self.add_part(BodyPart())
self.add_part(ArmPart())
self.add_part(LegPart())
class BirdRobot(Robot):
def create_robot(self):
self.add_part(HeadPart())
self.add_part(BodyPart())
self.add_part(WingPart())
self.add_part(LegPart())
dog_robot = DogRobot()
dog_robot.assemble_part()
"""
팩토리 메서드
"""
# 추상클래스로 만든다.
# 필요한 메서드는 하위클래스에서 재정의한다.
class Car(ABC):
cartype = None
def __str__(self):
return self.cartype
class Santafe(Car):
def __init__(self):
self.cartype = "santafe"
class Sonata(Car):
def __init__(self):
self.cartype = "sonata"
# 팩토리의 상위클래스로 상위클래스에서는 팩토리에서 사용될 공통의 기능을 정의해준다.
class CarFactory(ABC):
# 팩토리에서 사용될 공통 개념
@abstractmethod
def create_car(self, name):
pass
def numbering(self):
print("numbering")
def washing(self):
print("washing")
# create_car메서드에 대한 구현을 하면 된다.
def sell_car(self, name):
self.numbering()
self.create_car(name)
self.washing()
# 팩토리에 해당한다.
# 들어오는 인자에 따라서 생성되는 객체가 달라진다.
# 실제 코드 상에서 if-else로 사용하면 코드가 지저분해진다. 팩토리클래스를 사용하면 깔끔해진다.
# 팩토리를 두고 팩토리의 조건에 따라 생성되는 객체가 달라진다.
# 여기서 조건은 들어오는 인자(name)에 해당한다.
class HyundaiCarFactory(CarFactory):
def create_car(self, name):
if name == "sonata":
car = Sonata()
elif name == "santafe":
car = Santafe()
return car
# 인자에 따라서 생성되는 객체가 달라지기 때문에 enum에 넣어서 관리하면 좀더 효율적인 관리가 가능하다.
factory = HyundaiCarFactory()
new_car = factory.create_car("sonata")
new_car2 = factory.create_car("santafe")
print(new_car)
print(new_car2)
"""
팩토리 메서드
"""
# 상품에 대한 추상클래스로 만든다.
# 필요한 메서드는 하위클래스에서 재정의한다.
class Car(ABC):
cartype = None
def __str__(self):
return self.cartype
class Santafe(Car):
def __init__(self):
self.cartype = "santafe"
class Sonata(Car):
def __init__(self):
self.cartype = "sonata"
class CarFactory(ABC):
@abstractmethod
def returnCar(self, name):
pass
class HyundaiCarFactory(CarFactory):
carmap = {}
def returnCar(self, name):
car = self.carmap.get(name)
if car == None:
if name == "tomas":
car = Sonata()
elif name == "james":
car = Santafe()
self.carmap[name] = car
return car
# 인스턴스를 관리하는 것이다.
# 없으면 생성하고, 있으면 있는 인스턴스를 반환한다.
# 키 값에 해당하는 유일한 인스턴스 일 경우 사용가능하다.
factory = HyundaiCarFactory()
# 상황에 따라(어떤 인자가 들어왔냐에 따라서) 다양한 인스턴스를 생성할 수 잇다.
# 어떤 인자가 들어왔냐에 따라서 다양한 인스턴스를 만들 수 있다.
# map에서 관리를 해서 동일한 인스턴스를 반환하기도 한다.
a = factory.returnCar("tomas")
b = factory.returnCar("tomas")
print(a)
print(b)
print(a==b)
서로 연관되거나 의존적인 객체들의 조합을 만드는 인터페이스를 제공하는 패턴이다. 팩토리 안에 내부적으로 또 다른 팩토리 객체를 생성한다.
큰틀(RobotFactory)의 팩토리가 존재한다.
팩토리를 통해서 연관된 세트(개와 새조합)가 만들어진다.
세트를 만들어주는 작은공장(ARobotFactory, BRobotFactory)이 있고, 작은공장을 통해서 인스턴스 세트(Big Dog, Slow Bird)이 만들어진다.
DB의 예로 들어서 생각해보자.
회사에서 오라클과 mysql을 사용하고 있다.
이때 오라클과 mysql에 대한 사용하는 코드를 분리해서 따로 작성하는 것이아니라 팩토리를 이용해서 관리할 수 있다.
팩토리만 바꾸어서 디비의 트렌잭션을 바꾸어 주는 것이다.
모델단위로 클래스를 만들고, 그 클래스안에서 작동할 큰 틀을 잡는다.
오라클과 mysql의 쿼리가 다르기 때문에 위에서 만든 클래스를 상속받아서 각각의 쿼리문이 담긴 클래스를 생성한다.
그리고 상위팩토리를 만든다.
상위팩토리에서는 하위클래스에서 동작할 큰틀을 만들어준다.
각 DB별로 동작하는 게 다르므로 오라클팩토리, mysql팩토리를 만든다.
오라클 팩토리을 선택하면 user와 product등의 모델이 오라클부분이 돌아간다.
mysql 팩토리을 선택하면 user와 product등의 모델이 mysql부분이 돌아간다.
여러인스턴스들이 하나의 조건에 의해서 세팅으로 진행될 떄 팩토리를 구현해서 팩토리가 돌아가게 하면 유연하게 설계를 할 수 있다.
"""
추상팩토리
"""
from abc import ABC, abstractmethod
# 팩토리의 상위클래스로 하위클래스(팩토리)가 어떤 동작을 하는지 큰 틀을 잡는다.
class RobotFactory(ABC):
@abstractmethod
def create_dog_robot(self):
pass
@abstractmethod
def create_bird_robot(self):
pass
# 하위클래스에서 실제 어떤 동작을 하는지 정의한다.
# A팩토리의 하나의 세트를 만든다.
class ARobotFactory(RobotFactory):
def create_dog_robot(self):
return BigDog()
def create_bird_robot(self):
return SlowBird()
# B팩토리의 하나의 세트를 만든다.
class BRobotFactory(RobotFactory):
def create_dog_robot(self):
return SmallDog()
def create_bird_robot(self):
return FastBird()
class Dog(ABC):
@abstractmethod
def bark(self):
pass
class BigDog(Dog):
def bark(self):
print("bigdog bark")
class SmallDog(Dog):
def bark(self):
print("smalldog bark")
class Bird(ABC):
@abstractmethod
def fly(self):
pass
class FastBird(Bird):
def fly(self):
print("fastbird fly")
class SlowBird(Bird):
def fly(self):
print("slowbird fly")