빌더 패턴
빌더 패턴은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴입니다.
주요 특징과 장점은 다음과 같습니다
1.
복잡한 객체 생성 과정 단순화
•
복잡한 객체를 step-by-step으로 생성할 수 있습니다.
•
각 단계를 메서드 체이닝으로 연결하여 가독성 높은 코드를 작성할 수 있습니다.
2.
유연성:
•
같은 생성 과정으로 서로 다른 표현 결과를 만들 수 있습니다.
•
생성 과정을 세밀하게 나눠 필요한 데이터만 선택적으로 설정할 수 있습니다.
3.
불변성:
•
객체 생성이 완료된 후에는 내부 상태를 변경할 수 없어 불변성을 보장합니다.
4.
가독성:
•
생성자의 매개변수가 많을 때 발생하는 가독성 문제를 해결합니다.
•
어떤 데이터에 어떤 값이 설정되는지 명확히 알 수 있습니다.
5.
확장성:
•
새로운 속성이 추가되어도 기존 코드를 변경하지 않고 빌더에 새로운 메서드를 추가하면 됩니다.
빌더 패턴의 구조는 다음과 같습니다
참고
•
빌더 인터페이스 (Builder Interface)
◦
모든 구상 빌더들이 따라야 하는 공통 인터페이스를 정의합니다.
◦
제품 생성의 각 단계를 추상 메서드로 선언합니다.
◦
이를 통해 서로 다른 유형의 빌더들이 동일한 생성 과정을 따르도록 보장합니다.
•
구상 빌더 (Concrete Builders)
◦
빌더 인터페이스를 실제로 구현하는 클래스들입니다.
◦
각 구상 빌더는 제품 생성 단계들을 자신만의 방식으로 구현합니다.
◦
중요한 점은, 구상 빌더들이 반드시 공통 인터페이스를 따르는 제품만을 생산할 필요는 없다는 것입니다. 이는 빌더 패턴의 유연성을 보여줍니다.
•
제품 (Products)
◦
빌더들에 의해 최종적으로 생성되는 객체들입니다.
◦
서로 다른 빌더들에 의해 생성된 제품들은 반드시 같은 클래스 계층 구조나 인터페이스를 공유할 필요가 없습니다.
◦
이는 빌더 패턴이 매우 다양한 유형의 객체들을 생성할 수 있음을 의미합니다.
•
디렉터 (Director) → 선택적
◦
빌더를 사용하여 객체를 생성하는 순서를 정의합니다.
◦
디렉터를 사용하면 특정 설정의 제품을 쉽게 재생성할 수 있습니다.
◦
복잡한 생성 과정을 캡슐화하여 클라이언트 코드를 단순화합니다.
빌더 패턴은 다음과 같은 상황에서 유용합니다
•
객체 생성 과정이 복잡하고 여러 단계를 거쳐야 할 때
•
같은 생성 과정으로 서로 다른 표현의 객체를 만들어야 할 때
•
객체 생성 코드를 제품의 코드와 분리해야 할 때
하지만 빌더 패턴은 객체 생성을 위한 별도의 Builder 클래스가 필요하므로, 클래스 수가 늘어나는 단점이 있습니다. 따라서 생성하려는 객체가 충분히 복잡할 때 사용하는 것이 좋습니다.
예를 들어, 복잡한 설정이 필요한 자동차 객체를 생성할 때 빌더 패턴을 사용하면, 엔진, 바퀴, 색상 등을 단계별로 설정하고 최종적으로 완성된 자동차 객체를 얻을 수 있습니다.
이 패턴을 통해 객체 생성의 복잡성을 관리하고, 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.
예제
refactoring.guru
자동차들의 단계별 생성과 해당 자동차 모델들에 맞는 사용자 설명서들
•
Product 클래스들
◦
Car: 최종적으로 생성될 자동차 객체.
◦
Manual: 자동차 매뉴얼 객체.
•
Builder 인터페이스
◦
Builder (추상 기본 클래스): 모든 빌더가 구현해야 할 메서드를 정의.
•
Concrete Builders
◦
CarBuilder: 실제 Car 객체를 생성하는 빌더.
◦
CarManualBuilder: Car의 매뉴얼을 생성하는 빌더.
•
Director
◦
Director: 빌더를 사용하여 객체를 생성하는 순서를 정의.
from abc import ABC, abstractmethod
# 자동차 클래스 정의
class Car:
def __init__(self):
self.seats = None
self.engine = None
self.trip_computer = None
self.gps = None
def __str__(self):
return f"자동차 스펙: {self.seats}개의 좌석, {self.engine} 엔진, " \
f"{'트립 컴퓨터 탑재' if self.trip_computer else '트립 컴퓨터 미탑재'}, " \
f"{'GPS 탑재' if self.gps else 'GPS 미탑재'}"
# 매뉴얼 클래스 정의
class Manual:
def __init__(self):
self.content = []
def add_content(self, content):
self.content.append(content)
def __str__(self):
return "\n".join(self.content)
# 빌더 추상 클래스 정의
class Builder(ABC):
@abstractmethod
def reset(self):
pass
@abstractmethod
def set_seats(self, number):
pass
@abstractmethod
def set_engine(self, engine):
pass
@abstractmethod
def set_trip_computer(self, has_trip_computer):
pass
@abstractmethod
def set_gps(self, has_gps):
pass
# 자동차 빌더 클래스 정의
class CarBuilder(Builder):
def __init__(self):
self.car = None
self.reset()
def reset(self):
self.car = Car()
def set_seats(self, number):
self.car.seats = number
def set_engine(self, engine):
self.car.engine = engine
def set_trip_computer(self, has_trip_computer):
self.car.trip_computer = has_trip_computer
def set_gps(self, has_gps):
self.car.gps = has_gps
def get_product(self):
product = self.car
self.reset()
return product
# 자동차 매뉴얼 빌더 클래스 정의
class CarManualBuilder(Builder):
def __init__(self):
self.manual = None
self.reset()
def reset(self):
self.manual = Manual()
def set_seats(self, number):
self.manual.add_content(f"이 자동차는 {number}개의 좌석이 있습니다.")
def set_engine(self, engine):
self.manual.add_content(f"이 자동차는 {engine} 엔진을 탑재하고 있습니다.")
def set_trip_computer(self, has_trip_computer):
if has_trip_computer:
self.manual.add_content("이 자동차는 트립 컴퓨터가 장착되어 있습니다.")
else:
self.manual.add_content("이 자동차는 트립 컴퓨터가 장착되어 있지 않습니다.")
def set_gps(self, has_gps):
if has_gps:
self.manual.add_content("이 자동차는 GPS가 장착되어 있습니다.")
else:
self.manual.add_content("이 자동차는 GPS가 장착되어 있지 않습니다.")
def get_product(self):
product = self.manual
self.reset()
return product
# 디렉터 클래스 정의
class Director:
# 빌더를 사용하여 객체를 생성하는 순서를 정의
def construct_sports_car(self, builder):
builder.reset()
builder.set_seats(2)
builder.set_engine("스포츠 엔진")
builder.set_trip_computer(True)
builder.set_gps(True)
def construct_suv(self, builder):
builder.reset()
builder.set_seats(5)
builder.set_engine("SUV 엔진")
builder.set_trip_computer(True)
builder.set_gps(True)
# 메인 실행 코드
if __name__ == "__main__":
director = Director()
# 스포츠카 생성
car_builder = CarBuilder()
director.construct_sports_car(car_builder)
car = car_builder.get_product()
print("생성된 자동차 (Car built):")
print(car)
# 스포츠카 매뉴얼 생성
manual_builder = CarManualBuilder()
director.construct_sports_car(manual_builder)
manual = manual_builder.get_product()
print("\n생성된 자동차 매뉴얼 (Car manual built):")
print(manual)
# SUV 생성
print("\nSUV 생성 중 (Building an SUV):")
director.construct_suv(car_builder)
suv = car_builder.get_product()
print(suv)
Python
복사