[kotlin] 코틀린 빌더 패턴의 다양한 디자인 예시

코틀린은 강력한 빌더 패턴을 지원하여 객체 생성을 효과적으로 관리할 수 있게 해줍니다. 빌더 패턴은 객체의 생성 과정을 단계적으로 추상화하는 방법으로, 가독성과 유연성을 제공합니다. 이번 글에서는 코틀린에서 다양한 빌더 패턴을 어떻게 구현할 수 있는지 살펴보겠습니다.

1. 단일 객체 빌더 패턴

먼저, 단일 객체를 생성하는 빌더 패턴을 알아보겠습니다. 예를 들어, 사용자 정보를 담은 User 클래스를 생성하는 경우를 생각해봅시다.

class User private constructor(
    val username: String,
    val email: String,
    val age: Int
) {
    // User 클래스의 필수 매개변수들을 빌더로 받아옴
    class Builder(
        private val username: String,
        private val email: String
    ) {
        private var age: Int = 0
        
        // 선택적 매개변수들을 빌더로 설정
        fun age(age: Int) = apply { this.age = age }
        
        // User 객체를 생성하여 반환
        fun build() = User(username, email, age)
    }
}

이렇게 정의된 User 클래스는 Builder 클래스를 통해 생성됩니다. 필수 매개변수인 usernameemail은 빌더의 생성자로 전달되며, 선택적 매개변수로 age를 설정할 수 있습니다. apply 함수를 사용하여 빌더 객체의 내부 상태를 수정하고, build 함수를 호출하여 실제 User 객체를 생성합니다.

val user = User.Builder("John Doe", "john.doe@example.com")
    .age(25)
    .build()

위와 같이 생성된 user 객체는 불변성을 유지하면서, 필요한 속성을 설정하여 생성할 수 있습니다.

2. 중첩 객체 빌더 패턴

다음으로, 중첩된 객체를 생성하는 빌더 패턴을 알아보겠습니다. 예를 들어, AddressUser 객체를 함께 생성하는 경우를 생각해봅시다.

class User private constructor(
    val username: String,
    val email: String,
    val age: Int,
    val address: Address?
) {
    data class Address(
        val street: String,
        val city: String,
        val zipCode: String
    )
    
    class Builder(
        private val username: String,
        private val email: String
    ) {
        private var age: Int = 0
        private var address: Address? = null
        
        fun age(age: Int) = apply { this.age = age }
        
        // Address 객체를 생성하는 중첩 빌더
        fun address(block: Address.Builder.() -> Unit) {
            address = Address.Builder().apply(block).build()
        }
        
        fun build() = User(username, email, age, address)
    }
    
    // 중첩된 Address.Builder 클래스
    class Address.Builder {
        lateinit var street: String
        lateinit var city: String
        lateinit var zipCode: String
        
        fun build() = Address(street, city, zipCode)
    }
}

이 예시에서는 User 클래스 내부에 Address 클래스가 중첩되어 있습니다. Address 객체를 생성하기 위해 중첩된 Address.Builder 클래스를 정의하였습니다. address 함수에서 Address.Builder를 호출하고, 빌더 객체에 필요한 필드들을 설정한 후 build 함수를 호출하여 Address 객체를 생성합니다.

val user = User.Builder("John Doe", "john.doe@example.com")
    .age(25)
    .address {
        street = "123 Main St"
        city = "New York"
        zipCode = "10001"
    }
    .build()

위와 같이 생성된 user 객체는 Address 객체를 함께 가지고 있으며, 중첩된 빌더를 사용하여 필요한 속성을 설정할 수 있습니다.

결론

코틀린의 빌더 패턴은 객체 생성을 위한 강력한 도구입니다. 단일 객체 빌더 패턴과 중첩된 객체 빌더 패턴의 예시를 살펴봤는데, 이 외에도 다양한 방식으로 빌더 패턴을 구현할 수 있습니다. 코틀린의 빌더 패턴은 코드 가독성을 향상시키고, 객체 생성 과정을 유연하게 관리할 수 있게 해줍니다.


참고 자료: