PHpullh
학습 라이브러리/Kotlin/비지터 패턴 (Visitor)

KOTLIN · 디자인패턴

비지터 패턴 (Visitor)

sealed class와 when을 활용하여 객체 구조를 변경하지 않고 새 연산을 추가하는 비지터 패턴입니다.

디자인패턴고급visitorsealed-classpattern-matchingexpression

핵심 설명

sealed class와 when을 활용하여 객체 구조를 변경하지 않고 새 연산을 추가하는 비지터 패턴입니다.

Kotlin code

sealed class Expr {
    data class Num(val value: Double) : Expr()
    data class Add(val left: Expr, val right: Expr) : Expr()
    data class Mul(val left: Expr, val right: Expr) : Expr()
    data class Neg(val expr: Expr) : Expr()
}

// 비지터: 새 연산 추가 (클래스 수정 불필요)
fun eval(expr: Expr): Double = when (expr) {
    is Expr.Num -> expr.value
    is Expr.Add -> eval(expr.left) + eval(expr.right)
    is Expr.Mul -> eval(expr.left) * eval(expr.right)
    is Expr.Neg -> -eval(expr.expr)
}

fun prettyPrint(expr: Expr): String = when (expr) {
    is Expr.Num -> expr.value.toString()
    is Expr.Add -> "(${prettyPrint(expr.left)} + ${prettyPrint(expr.right)})"
    is Expr.Mul -> "(${prettyPrint(expr.left)} * ${prettyPrint(expr.right)})"
    is Expr.Neg -> "-${prettyPrint(expr.expr)}"
}

fun depth(expr: Expr): Int = when (expr) {
    is Expr.Num -> 0
    is Expr.Add -> 1 + maxOf(depth(expr.left), depth(expr.right))
    is Expr.Mul -> 1 + maxOf(depth(expr.left), depth(expr.right))
    is Expr.Neg -> 1 + depth(expr.expr)
}

fun main() {
    // (3 + 4) * -(2)
    val expr = Expr.Mul(
        Expr.Add(Expr.Num(3.0), Expr.Num(4.0)),
        Expr.Neg(Expr.Num(2.0))
    )
    println("식: ${prettyPrint(expr)}")
    println("값: ${eval(expr)}")
    println("깊이: ${depth(expr)}")
}

학습 팁

Kotlin에서는 sealed class + when 조합이 전통적인 Visitor 인터페이스보다 간결합니다. 새 연산은 함수를 추가하면 됩니다.

주의할 점

sealed class에 새 하위 타입을 추가하면 모든 when 분기를 수정해야 합니다. 타입이 자주 바뀌면 전통적인 OOP 방식이 나을 수 있습니다.

자주 묻는 질문

비지터 패턴 (Visitor)란 무엇인가요?

sealed class와 when을 활용하여 객체 구조를 변경하지 않고 새 연산을 추가하는 비지터 패턴입니다.

비지터 패턴 (Visitor) 학습 시 주의할 점은 무엇인가요?

sealed class에 새 하위 타입을 추가하면 모든 when 분기를 수정해야 합니다. 타입이 자주 바뀌면 전통적인 OOP 방식이 나을 수 있습니다.