[Effective Kotlin] 아이템 34. 기본 생성자에 이름 있는 옵션 아규먼트를 사용하라

Jimin Lim·2023년 9월 4일
0

Effective Kotlin

목록 보기
34/39
post-thumbnail

아이템 34

기본 생성자에 이름 있는 옵션 아규먼트를 사용하라

점층적 생성자 패턴

'여러 가지 종류의 생성자를 사용하는' 간단한 패턴

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)

일반적으로 점층적 생성자 패턴보다 디폴트 아규먼트가 다양한 기능을 제공한다. 디폴트 아규먼트의 장점은 다음과 같다.

  1. 파라미터 값을 원하는 대로 지정할 수 있다.
  2. 아규먼트를 원하는 순서로 지정할 수 있다.
  3. 명시적으로 이름을 붙여서 아규먼트를 지정하므로 의미가 훨씬 명확하다.

빌더 패턴

자바에서 주로 사용하는 패턴이고, 다음과 같은 장점이 있다.

  1. 파라미터에 이름을 붙일 수 있다.
  2. 파라미터를 원하는 순서로 지정할 수 있다.
  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()
}

이름있는 아규먼트 방식의 장점

빌더 패턴의 장점은 이름있는 아규먼트 방식도 가지고 있으며 굳이 빌더패턴을 만들기보단 이름있는 아규먼트 방식을 사용하는 것이 낫다. 그 이유는 다음과 같다.

  1. 더 짧다: 파라미터를 변경하면 내부 함수명도 변경해야한다.
  2. 더 명확하다: 빌더 패턴은 객체 생성 방법을 여러 메서드를 확인해야한다.
  3. 더 사용하기 쉽다: 언어레벨과 직접구현 중에서 언어레벨에서 제공하는 것이 더 사용하기 쉽다.
  4. 동시성 문제가 없다: 코틀린 함수 파라미터는 항상 immutable한데 빌더 패턴의 프로퍼티는 mutable하다.

빌더 패턴의 장점

반대로 값의 의미를 묶어서 사용할 때 빌더 패턴이 나을 수도 있다. 하지만 이또한 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
}

정리

  • 빌더, 점층적 생성자 패턴을 사용하지 않는다.
  • 디폴트 아규먼트를 갖는 기본 생성자, DSL을 주로 사용한다.
profile
💻 ☕️ 🏝 🍑 🍹 🏊‍♀️

0개의 댓글