builder-pattern
TL;DR⚑
- 복잡한 객체의 생성 과정을 단계별로 나누어 처리하며, 동일한 생성 절차에서 서로 다른 표현을 만들 수 있도록 하는 패턴.
- 클라이언트 입장에서 생성 과정을 알아야하면 Builder. 생성 과정을 숨기고 싶으면 Factory.
활용성⚑
- 복잡한 객체를 생성해야 하고, 다양한 구성 요소가 조합되어 객체를 완성해야 할 때.
- 동일한 객체 생성 과정에서 서로 다른 표현을 만들고자 할 때.
- 객체 생성 과정에서 유연성과 가독성을 높이고자 할 때.
결과⚑
-
장점
- 복잡한 객체 생성의 분리: 객체 생성 코드를 분리하여 객체의 생성 과정이 코드의 다른 부분과 분리됨
- 가독성 향상: 단계별로 객체를 생성하므로 코드의 가독성과 유지보수성이 향상
- 다양한 표현 가능: 빌더를 통해 동일한 객체 생성 절차에서 서로 다른 표현을 만들 수 있음
-
단점
- 설계 복잡성 증가: 간단한 객체 생성에는 불필요할 수 있으며, 코드 복잡성을 증가시킴
- 가변성 증가: 객체의 다양한 표현을 지원하기 위해 여러 개의 빌더가 필요할 수 있어 관리가 복잡해짐
구현⚑
- Director: 객체 생성의 절차를 관리. 빌더에게 객체 생성을 지시하며, 각 단계의 실행 순서를 제어
- Builder: 객체 생성을 위한 인터페이스를 제공. 이 인터페이스를 구현한 구체적인 빌더들이 실제 객체를 구성
- Concrete Builder: Builder 인터페이스를 구현하여 구체적인 객체를 생성. 각 빌더는 특정한 객체 생성을 책임짐
- Product: 최종적으로 생성되는 복잡한 대상 객체
Code Example
예시⚑
from abc import ABC, abstractmethod
# Product 클래스: WebPage
class WebPage:
def __init__(self):
self.header: Header = None
self.body: List[BodySection] = []
self.footer: Footer = None
def __str__(self):
body_str = "\n".join([str(section) for section in self.body])
return f"Header: {self.header}\n\nBody:\n{body_str}\n\nFooter: {self.footer}"
# 중첩된 클래스들: Header, BodySection, Footer
class Header:
def __init__(self, logo, menu):
self.logo = logo
self.menu = menu
def __str__(self):
return f"Logo: {self.logo}, Menu: {', '.join(self.menu)}"
class BodySection:
def __init__(self, title, content):
self.title = title
self.content = content
def __str__(self):
return f"Title: {self.title}, Content: {self.content}"
class Footer:
def __init__(self, copyright_info, links):
self.copyright_info = copyright_info
self.links = links
def __str__(self):
return f"Copyright: {self.copyright_info}, Links: {', '.join(self.links)}"
# Builder 인터페이스
class WebPageBuilder(ABC):
@abstractmethod
def build_header(self, logo, menu):
pass
@abstractmethod
def add_body_section(self, title, content):
pass
@abstractmethod
def build_footer(self, copyright_info, links):
pass
@abstractmethod
def get_webpage(self) -> WebPage:
pass
# Concrete Builder
class ConcreteWebPageBuilder(WebPageBuilder):
def __init__(self):
self.webpage = WebPage()
def build_header(self, logo, menu):
self.webpage.header = Header(logo, menu)
def add_body_section(self, title, content):
self.webpage.body.append(BodySection(title, content))
def build_footer(self, copyright_info, links):
self.webpage.footer = Footer(copyright_info, links)
def get_webpage(self) -> WebPage:
return self.webpage
# Director
class WebPageDirector:
def __init__(self, builder: WebPageBuilder):
self._builder = builder
def construct_home_page(self):
self._builder.build_header("My Logo", ["Home", "About", "Contact"])
self._builder.add_body_section("Welcome", "Welcome to our homepage!")
self._builder.add_body_section("Features", "We offer a wide range of services.")
self._builder.build_footer("© 2024 My Company", ["Privacy", "Terms of Service"])
def construct_about_page(self):
self._builder.build_header("My Logo", ["Home", "About", "Contact"])
self._builder.add_body_section("About Us", "We are a leading company in our industry.")
self._builder.add_body_section("Our Mission", "To deliver high-quality products.")
self._builder.build_footer("© 2024 My Company", ["Careers", "Contact Us"])
# 사용 예시
if __name__ == "__main__":
builder = ConcreteWebPageBuilder()
director = WebPageDirector(builder)
# 홈 페이지 생성
director.construct_home_page()
home_page = builder.get_webpage()
print("Home Page:\n", home_page)
# 어바웃 페이지 생성
builder = ConcreteWebPageBuilder() # 새 빌더로 초기화
director = WebPageDirector(builder)
director.construct_about_page()
about_page = builder.get_webpage()
print("\nAbout Page:\n", about_page)
요약⚑
- 빌더 패턴은 복잡한 객체를 생성할 때 유용한 패턴으로, 객체 생성 과정과 객체를 표현하는 방식을 분리할 수 있습니다.
- 이 패턴을 사용하면 동일한 절차를 통해 다양한 객체를 생성할 수 있으며, 객체 생성 로직을 분리하여 코드의 가독성과 유지보수성을 높일 수 있습니다.
- 그러나 간단한 객체 생성에는 오히려 복잡성을 증가시킬 수 있으므로, 필요에 따라 적절하게 사용하는 것이 중요합니다.
Director와 Factory의 차이점⚑
Director
는Factory
객체와 비슷하게 느껴질 수 있지만, 역할과 목적이 다름.-
둘 다 객체 생성과 관련이 있지만, 그 방식과 사용하는 시나리오에서 중요한 차이가 있음
-
목적:
- Director
- 빌더 패턴에서
Director
의 주된 역할은 객체를 생성하는 과정(절차)을 정의하고, 그 절차에 따라 객체를 구성하는 것 Director
는 객체 생성의 순서와 단계를 관리하지만, 객체 생성 자체의 논리보다는 객체가 어떻게 구성되는지에 중점을 둡니다.
- 빌더 패턴에서
- Factory
Factory
패턴에서Factory
는 객체 생성의 책임을 캡슐화합니다.- 어떤 종류의 객체를 생성할지를 결정하고, 생성된 객체를 반환합니다.
Factory
는 객체를 생성하는 로직 자체를 캡슐화하며, 클라이언트는 객체 생성의 복잡한 과정에 대해 알 필요가 없습니다.
- Director
-
역할:
- Director
- 빌더 패턴의
Director
는 여러 단계를 거쳐 복잡한 객체를 구성하는 데 사용 Director
는 빌더에게 어떤 단계에서 어떤 작업을 수행할지를 지시하고, 각 단계를 거쳐 최종 객체를 완성 시킴 -Director
는 빌더가 제공하는 인터페이스를 통해 작업을 수행하며, 객체가 생성되는 절차를 제어
- 빌더 패턴의
- Factory
Factory
는 특정 유형의 객체를 생성하는 데 필요한 정보를 알고 있으며, 해당 객체를 직접 생성하여 반환Factory
는 객체의 생성 과정을 클라이언트 코드로부터 숨기고, 클라이언트는 단순히Factory
를 호출하여 객체를 받을 뿐
- Director
-
사용 시나리오:
- Director
- 복잡한 객체를 구성하는 여러 단계를 관리할 필요가 있을 때
- 예를 들어, 다양한 옵션이 있는 복잡한 제품을 만들거나, 여러 단계에 걸쳐 객체를 설정해야 할 때 사용
- Factory
- 객체를 생성하는 로직이 복잡하거나, 생성할 객체의 유형이 런타임에 결정될 때
- 예를 들어, 구체적인 클래스를 미리 알 수 없고, 상황에 따라 다른 객체를 생성해야 할 때
Factory
패턴 사용
- Director
Factory vs Builder Code Example
1. Director 예시 (빌더 패턴)⚑
# Builder 인터페이스
class CarBuilder(ABC):
@abstractmethod
def build_engine(self):
pass
@abstractmethod
def build_wheels(self):
pass
@abstractmethod
def build_frame(self):
pass
@abstractmethod
def get_car(self) -> Car:
pass
# Concrete Builder
class SportsCarBuilder(CarBuilder):
def __init__(self):
self.car = Car()
def build_engine(self):
self.car.engine = "V8 Engine"
def build_wheels(self):
self.car.wheels = "Sports Wheels"
def build_frame(self):
self.car.frame = "Lightweight Frame"
def get_car(self) -> Car:
return self.car
# Director
class CarDirector:
def __init__(self, builder: CarBuilder):
self._builder = builder
def construct_sports_car(self):
self._builder.build_frame()
self._builder.build_engine()
self._builder.build_wheels()
return self._builder.get_car()
# 사용 예시
builder = SportsCarBuilder()
director = CarDirector(builder)
car = director.construct_sports_car()
여기서 Director
는 객체 생성의 절차를 관리하고, Builder
를 통해 단계를 수행
2. Factory 예시 (Factory 패턴)⚑
class CarFactory:
def create_car(self, car_type: str) -> Car:
if car_type == "sports":
return Car(engine="V8 Engine", wheels="Sports Wheels", frame="Lightweight Frame")
elif car_type == "sedan":
return Car(engine="V6 Engine", wheels="Standard Wheels", frame="Standard Frame")
else:
raise ValueError("Unknown car type")
# 사용 예시
factory = CarFactory()
sports_car = factory.create_car("sports")
sedan_car = factory.create_car("sedan")
여기서 Factory
는 클라이언트가 요청한 유형의 객체를 직접 생성하고 반환