기본 생성자에 이름 있는 옵션 아규먼트를 사용하라
'여러 가지 종류의 생성자를 사용하는' 간단한 패턴
class Pizza {
val size: String
val cheese: Int
val olives: Int
val bacon: Int
constructor(size: String, cheese: Int, olives: Int, bacon: Int) {
this.size = size
this.cheese = cheese
this.olives = olives
this.bacon = bacon
}
constructor(size: String, cheese: Int, olives: Int):
this(size, cheese, olives, 0)
constructor(size: String, cheese: Int): this(size, cheese, 0)
constructor(size: String): this(size, 0)
}
class Pizza(
val size: String,
val cheese: Int = 0,
val olives: Int = 0,
val bacon: Int = 0,
)
val myFavorite = Pizza("L", olives = 3)
일반적으로 점층적 생성자 패턴보다 디폴트 아규먼트가 다양한 기능을 제공한다. 디폴트 아규먼트의 장점은 다음과 같다.
자바에서 주로 사용하는 패턴이고, 다음과 같은 장점이 있다.
class Pizza private constructor(
val size: String,
val cheese: Int = 0,
val olives: Int = 0,
val bacon: Int = 0,
) {
class Builder(private val size: String) {
private var cheese: Int = 0
private var olives: Int = 0
private var bacon: Int = 0
fun setCheese(value: Int): Builder = apply {
cheese = value
}
fun setOlives(value: Int): Builder = apply {
olives = value
}
fun setBacon(value: Int): Builder = apply {
bacon = value
}
fun build() = Pizza(size, cheese, olives, bacon)
}
}
fun main() {
val myFavorite = Pizza.Builder("L").setOlives(3).build()
val villagePizza = Pizza.Builder("L")
.setCheese(1)
.setOlives(2)
.setBacon(3)
.build()
}
빌더 패턴의 장점은 이름있는 아규먼트 방식도 가지고 있으며 굳이 빌더패턴을 만들기보단 이름있는 아규먼트 방식을 사용하는 것이 낫다. 그 이유는 다음과 같다.
반대로 값의 의미를 묶어서 사용할 때 빌더 패턴이 나을 수도 있다. 하지만 이또한 DSL로 대체 가능하다.
//(1) 빌더 패턴
val router = Router.Builder()
.addRoute(path = "/home", ::showHome)
.addRoute(path = "/users", ::showUsers)
.build()
//(2) 이름있는 아규먼트, 중간에 추가적인 타입을 만들고 있다.
val router = Router(
routes = listOf(
Route("/home", ::showHome),
Route("/users", ::showUsers)
)
//(3) DSL, (2)방식을 보완할 방법
val route = router {
"/home" directsTo :: showHome
"/users" directsTo :: showUsers
}