오버로딩을 통해 여러 생성자를 이용하는 방법
👉 여전히 각 인자가 어떤 값을 나타내는지 몰라 가독성 면에서 단점이 존재
setter 메소드를 이용해 값을 설정하는 방법(setter 메소드는 this를 리턴)
👉 setName() 과 같은 메소드를 사용해 가독성 문제는 해결되었지만 동시에 해당 객체에 접근할 수 있는 환경이라면?(ex. 멀티스레드 환경) 객체의 일관성이 깨질 수 있음 또, 각 멤버변수와 객체 자체가 immutable할 수 없음
점층적 생성자 패턴의 장점과 자바빈 패턴의 장점을 합쳐보자!
이와 같이 동작하는 Builder Pattern이란,
복잡한 객체를 생성하는 방식 / 객체를 표현하는 방법을 분리해 다양한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴
즉, 복잡한 객체의 단계적 생성에 중점을 둔 패턴임
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
}
BuilderPatterm - github.com/cchloe2311
👉 atomicity, thread-safety 보장 확인!
https://www.geeksforgeeks.org/builder-pattern-in-java/
https://stackoverflow.com/questions/36140791/how-to-implement-builder-pattern-in-kotlin