[디자인패턴] Builder Pattern, 빌더 패턴

Chloe Choi·2021년 2월 17일
0

디자인패턴

목록 보기
5/11

Builder Pattern 설명

Why

  • 생성자에 많은 인자가 있는 경우
  • 객체 생성 시 선택 인자가 있는 경우(여러 생성자를 갖게 됨)
    👉 각 인자가 어떤 값을 나타내는지 어려워 가독성이 안좋아짐

Sol1. 점층적 생성자 패턴

오버로딩을 통해 여러 생성자를 이용하는 방법
👉 여전히 각 인자가 어떤 값을 나타내는지 몰라 가독성 면에서 단점이 존재

Sol2. 자바빈 패턴

setter 메소드를 이용해 값을 설정하는 방법(setter 메소드는 this를 리턴)
👉 setName() 과 같은 메소드를 사용해 가독성 문제는 해결되었지만 동시에 해당 객체에 접근할 수 있는 환경이라면?(ex. 멀티스레드 환경) 객체의 일관성이 깨질 수 있음 또, 각 멤버변수와 객체 자체가 immutable할 수 없음

How

점층적 생성자 패턴의 장점과 자바빈 패턴의 장점을 합쳐보자!

흐름

  1. 클라이언트는 필수 인자를 전달해 빌더 객체 생성(생성자 호출)
  2. 빌더 개체의 setter 메소드를 이용해 선택 인자 전달
  3. build() 메소드를 통해 빌더 객체 내부에서 Product 객체를 생성, return

효과

  • 1, 2로 필수 인자와 선택 인자를 구분해 선택 인자는 가독성이 좋은 코드로 넘길 수 있음
  • 객체를 생성하는 방식과 객체를 표현하는 방법을 분리해 객체 생성을 유연하게 할 수 있음
  • Product 자체의 생성은 build() 함수에서 한 번에 진행되기 때문에 atomicity, thread-safety 보장

What

이와 같이 동작하는 Builder Pattern이란,

복잡한 객체를 생성하는 방식 / 객체를 표현하는 방법을 분리해 다양한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴

즉, 복잡한 객체의 단계적 생성에 중점을 둔 패턴임

구현

  • Builder: Product 클래스의 static nested class, build() 메소드는 outer class의 object 리턴
  • Product를 volatile 키워드를 사용해 선언 👉 가시성 보장

Kotlin

Kotlin 에서는 기본값과 named argument를 제공해서 빌더 클래스를 따로 구현할 필요가 없음

// 클래스 정의
class Car(val model: String? = null, val year: Int = 0)

// 사용
val car = Car(model = "X")

위와 같이 사용해도 충분하지만, 꼭 빌더 클래스를 만들어야겠다면 아래와 같이 구현, 사용 가능

class Car( //add private constructor if necessary
        val model: String?,
        val year: Int
) {

    private constructor(builder: Builder) : this(builder.model, builder.year)

    class Builder {
        var model: String? = null
            private set

        var year: Int = 0
            private set

        fun model(model: String) = apply { this.model = model }

        fun year(year: Int) = apply { this.year = year }

        fun build() = Car(this)
    }
}

// 사용
val car = Car.Builder().model("X").build()

// +) DSL 사용 시
class Car (
        val model: String?,
        val year: Int,
        val required: String
) {

    private constructor(builder: Builder) : this(builder.model, builder.year, builder.required)

    companion object {
        inline fun build(required: String, block: Builder.() -> Unit) = Builder(required).apply(block).build()
    }

    class Builder(
            val required: String
    ) {
        var model: String? = null
        var year: Int = 0

        fun build() = Car(this)
    }
}

// 사용 (확장함수처럼 사용)
val car = Car.build(
    required = "requiredValue"
) {
    model = "X"
    year = 1
}

예제 Repository

BuilderPatterm - github.com/cchloe2311

  • Java 결과화면
  • Kotlin 결과화면

👉 atomicity, thread-safety 보장 확인!

✍️ ref

https://www.geeksforgeeks.org/builder-pattern-in-java/
https://stackoverflow.com/questions/36140791/how-to-implement-builder-pattern-in-kotlin

profile
똑딱똑딱

0개의 댓글