Menu
Coddy logo textTech

빌더 패턴

Coddy Kotlin 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 48번째.

빌더 패턴은 객체를 단계별로 생성하고 마지막에 전체를 검사합니다. 빌더는 빌더 자체를 반환하는 체이닝 가능한 메서드에 각 부분을 모으며, build()가 완성된 객체를 생성합니다:

data class Email(val to: List<String>, val subject: String)

class EmailBuilder {
    private val recipients = mutableListOf<String>()
    private var subject = ""

    fun to(address: String): EmailBuilder {
        recipients.add(address)
        return this
    }
    fun subject(text: String): EmailBuilder {
        subject = text
        return this
    }
    fun build(): Email {
        check(recipients.isNotEmpty()) { "no recipients" }
        return Email(recipients.toList(), subject)
    }
}

main 내부:

val mail = EmailBuilder().to("ada@x.io").to("bo@x.io").subject("Lunch").build()
println(mail)

출력:

Email(to=[ada@x.io, bo@x.io], subject=Lunch)

apply는 모든 단계를 간결하게 만듭니다. 객체를 this로 사용하여 블록을 실행하고 객체를 반환하므로, 한 단계가 한 줄이 됩니다. check(condition) { message }는 객체의 상태에 대해 require에 대응하는 IllegalStateException을 발생시킵니다.

class RequestBuilder {
    var url = ""
    var method = "GET"
    private val headers = mutableListOf<String>()

    fun header(name: String, value: String) = apply { headers.add("$name: $value") }

    fun build(): String {
        check(url.isNotEmpty()) { "no url" }
        return "$method $url " + headers
    }
}

main 내부에서:

val request = RequestBuilder().apply {
    url = "/users"
    method = "POST"
    header("Accept", "json")
}.build()
println(request)

출력:

POST /users [Accept: json]

수신 객체가 있는 람다를 인수로 받는 함수는, 타입이 Builder.() -> Unit일 때 빌더를 작은 DSL(도메인 특화 언어)로 바꿉니다. 중괄호 안에서는 빌더가 this이므로 호출자는 설정만 작성하면 됩니다:

class MenuBuilder(private val title: String) {
    private val items = mutableListOf<String>()

    fun item(name: String, cents: Int) {
        items.add("$name %d.%02d".format(cents / 100, cents % 100))
    }

    fun build() = "== $title ==\n" + items.joinToString("\n")
}

fun menu(title: String, block: MenuBuilder.() -> Unit): String = MenuBuilder(title).apply(block).build()

main 내부:

val lunch = menu("Lunch") {
    item("soup", 450)
    item("salad", 600)
}
println(lunch)

출력:

== Lunch ==
soup 4.50
salad 6.00

Kotlin의 이름 있는 인수와 기본 인수는 간단한 경우를 이미 처리합니다. Pizza(size = 30, cheese = true)는 호출 위치에서 모든 선택 사항의 이름을 지정합니다. 객체를 여러 단계에 걸쳐 구성하거나, 목록을 수집하거나, 객체가 생성되기 전에 필드 간 검사가 필요한 경우에는 빌더가 유용합니다:

data class Pizza(val size: Int = 26, val cheese: Boolean = true, val olives: Boolean = false)
val simple = Pizza(size = 30, olives = true)        // 빌더가 필요 없음

val mail = EmailBuilder()                            // 단계별 조립을 위한 빌더
    .to("ada@x.io")
    .to("bo@x.io")                                  // 리스트를 수집함
    .subject("Plan")
    .build()                                        // 마지막에 모든 것을 확인함
challenge icon

챌린지

쉬움

제공된 데이터 클래스 Email를 위한 빌더를 작성하세요. EmailBuilder에는 연결 가능한 단계가 있습니다: to(address)(여러 번 호출할 수 있으며 수신자를 추가함), subject(text), body(text) 및 urgent(). build()는 Email을 반환하거나 IllegalStateException을 발생시키며, 메시지는 no recipients 또는 no subject입니다(검사는 이 순서대로 수행). 긴급 이메일의 제목 앞에는 [URGENT] 가 추가됩니다.

제공된 코드는 명령(to ada@x.io, subject Hello, body text, urgent, send)을 읽고, 각 --- 줄 다음에 새 빌더를 시작하며, 생성된 이메일 To: ada@x.io, bo@x.io | [URGENT] Hello | text 또는 error: no subject를 출력합니다.

코드는 EmailBuilder.kt와 Email.kt에 작성하세요. Main.kt에는 제공된 입력/출력 코드가 포함되어 있으며 편집할 수 없습니다.

직접 해보기

fun main() {
    // 제공된 입력/출력 코드: 그대로 유지하세요
    val input = generateSequence(::readLine).toList()
    var builder = EmailBuilder()
    for (cmd in input) {
        when {
            cmd == "send" -> {
                try {
                    println(builder.build())
                } catch (e: IllegalStateException) {
                    println("error: ${e.message}")
                }
            }
            cmd == "---" -> builder = EmailBuilder()
            cmd == "urgent" -> builder.urgent()
            cmd.startsWith("to ") -> builder.to(cmd.drop(3))
            cmd.startsWith("subject ") -> builder.subject(cmd.drop(8))
            else -> builder.body(cmd.drop(5))
        }
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: Kotlin 플레이그라운드