소프트웨어 개발자/디자인패턴

[디자인패턴] 생성 패턴 - 빌더 (Builder) 패턴

yubi5050 2024. 3. 30. 17:31

생성 (Creational) 패턴

다양한 상황에서의 객체의 생성 방식을 결정하는 디자인 패턴

 

주요 패턴으론 다음 사항들이 있다.

  • 팩토리 메서드 (Factory Method)
  • 추상 팩토리 (Abstract Factory)
  • 빌더(Builder) <- 이번 글에서 다룰 내용
  • 싱글톤(SingleTon) 
  • 프로토타입(Prototype)  
  • 템플릿메서드(TemplateMethod)

 

빌더 패턴 이란? 

  • 생성 패턴의 한 방법으로, 복잡한 객체를 단계별로 생성할 있도록 하는 디자인 패턴
  • 같은 생성(construction) 코드를 사용하여 다양한 타입과 상태의 객체 만들 있다.
  • 아래 사진 처럼 다양한 종류의 House를 만들기 쉬움
  • Directory 개념을 추가해, 빌더 단계들에 대한 일련의 호출 순서를 정의 가능

 

https://refactoring.guru/images/patterns/diagrams/builder/problem1-2x.png

 

인터페이스 구조 

  • 집의 벽, 문, 지붕, 창문, 주차장을 원하는 파라미터로 만드는 함수
  • 집 객체를 리턴하는 함수

 

https://refactoring.guru/images/patterns/diagrams/builder/solution1-2x.png?id=a9c2ab02f0b2aca1a7512022194dd113

 

 

예시 코드

# 유저 객체
class User:
    def __init__(self):
        self.user_name = None
        self.password = None
        self.email = None

# 유저 빌더
class UserBuilder:
    def __init__(self):
        self.user = User()

    def set_user_name(self, user_name):
        self.user.user_name = user_name
        return self

    def set_password(self, password):
        self.user.password = password
        return self

    def set_email(self, email):
        self.user.email = email
        return self

    def build(self):
        return self.user


if __name__ == "__main__":
    user = UserBuilder() \
        .set_user_name('smkim') \
        .set_password('1234') \
        .set_email('smkim@naver.com') \
        .build()
    print(user)

 

vs 생성자 사용 방식

생성자 방식 

- User 객체에 비즈니스 로직이 추가되면 될 수록 생성자가 복잡해진다. 

- 비즈니스 로직을 분리할 수 있지만, SRP 원칙에 어긋난다.

 

빌더 방식 

- 비즈니스 로직이 UserBuilder에 옮겨진다.

- 코드량이 많아지지만 SRP 원칙을 지킬 수 있다.

# Constructor vs Builder - 생성자 방식
class User:
    def __init__(self, user_type, user_name):
        # user_name에 대한 복잡한 비즈니스 로직이 추가됨
        if user_type == 'admin':
            self.user_name = '관리자' + user_name
        else:
            self.user_name = '일반' + user_name


# Constructor vs Builder - 빌더 방식
class User:
    def __init__(self):
        self.user_type = None
        self.user_name = None

class UserBuilder:
    def __init__(self):
        self.user=User()

    def set_user_type(self, user_type):
        self.user.user_type = user_type

    def set_user_name(self, user_name):
        if self.user.user_type == 'admin':
            self.user.user_name = '관리자' + user_name
        else:
            self.user.user_name = '일반' + user_name
        return self



if __name__ == "__main__":
    # 생성자 vs 빌더 - 생성자 방식 1
    user = User(user_type='Admin', user_name='smkim')

	# 생성자 vs 빌더 - 생성자 방식 2
	user = User(user_type='Admin')
    user.set_user_name(user_name='smkim')

    # 생성자 vs 빌더 - 빌더 호출 방식
    user = UserBuilder() \
        .set_user_type('Admin') \
        .set_user_name('smkim') \
        .build()

장단점

장점

  • 객체 생성 과정을 유연하게 작성하고 가독성을 높일 수 있음 
  • SRP : 비즈니스 로직을 (User -> UserBuilder)분리 시킬 수 있음

 

단점

  • 여러 클래스(User, UserBuilder)가 필요해지므로, 코드의 복잡성이 증가함