본문 바로가기
공부/이펙티브코틀린

아이템39 - 태그 클래스보다는 클래스 계층을 사용하라

by 띵커베르 2024. 8. 1.
728x90

태그 클래스(tagged class): 상수 모드를 태그라고 부르고, 태그를 포함한 클래스

-> 객체 타입을 나타내기 위해 필드를 사용하고, 해당 필드에 따라 동작이 달라지는 클래스를 의미.

 

 

  • 태그 클래스의 문제점:
    • 복잡성 증가: 하나의 클래스에 여러 유형의 객체를 처리하기 위한 코드가 포함되어 복잡성이 증가합니다.
    • 유지보수 어려움: 새로운 유형이 추가될 때마다 기존 코드를 수정해야 하므로 유지보수가 어렵습니다.
    • 가독성 저하: 태그를 기반으로 분기하는 코드가 많아지면 코드의 가독성이 떨어집니다.
  • 클래스 계층의 장점:
    • 명확한 구분: 각 클래스가 자신의 책임을 가지므로 코드가 명확해집니다.
    • 확장성: 새로운 유형을 쉽게 추가할 수 있습니다.
    • 유지보수 용이: 각 클래스가 독립적이기 때문에 수정이 용이합니다.

 

태그 클래스 사용 예시:

enum class ShapeType {
    CIRCLE, RECTANGLE, TRIANGLE
}

class Shape(val type: ShapeType, val radius: Double = 0.0, val width: Double = 0.0, val height: Double = 0.0)

fun area(shape: Shape): Double {
    return when (shape.type) {
        ShapeType.CIRCLE -> Math.PI * shape.radius * shape.radius
        ShapeType.RECTANGLE -> shape.width * shape.height
        ShapeType.TRIANGLE -> 0.5 * shape.width * shape.height
    }
}

fun main() {
    val circle = Shape(ShapeType.CIRCLE, radius = 2.0)
    val rectangle = Shape(ShapeType.RECTANGLE, width = 2.0, height = 3.0)
    val triangle = Shape(ShapeType.TRIANGLE, width = 2.0, height = 4.0)

    println("Circle area: ${area(circle)}")
    println("Rectangle area: ${area(rectangle)}")
    println("Triangle area: ${area(triangle)}")
}

 

 

 

클래스 계층 사용 예시:

abstract class Shape {
    abstract fun area(): Double
}

class Circle(val radius: Double) : Shape() {
    override fun area() = Math.PI * radius * radius
}

class Rectangle(val width: Double, val height: Double) : Shape() {
    override fun area() = width * height
}

class Triangle(val base: Double, val height: Double) : Shape() {
    override fun area() = 0.5 * base * height
}

fun main() {
    val shapes: List<Shape> = listOf(
        Circle(2.0),
        Rectangle(2.0, 3.0),
        Triangle(2.0, 4.0)
    )

    for (shape in shapes) {
        println("Area: ${shape.area()}")
    }
}

 

 

sealed 클래스 사용 예시:

sealed class Shape {
    abstract fun area(): Double
}

class Circle(val radius: Double) : Shape() {
    override fun area() = Math.PI * radius * radius
}

class Rectangle(val width: Double, val height: Double) : Shape() {
    override fun area() = width * height
}

class Triangle(val base: Double, val height: Double) : Shape() {
    override fun area() = 0.5 * base * height
}

fun describeShape(shape: Shape): String {
    return when (shape) {
        is Circle -> "This is a circle with radius ${shape.radius}"
        is Rectangle -> "This is a rectangle with width ${shape.width} and height ${shape.height}"
        is Triangle -> "This is a triangle with base ${shape.base} and height ${shape.height}"
    }
}

fun main() {
    val shapes: List<Shape> = listOf(
        Circle(2.0),
        Rectangle(2.0, 3.0),
        Triangle(2.0, 4.0)
    )

    for (shape in shapes) {
        println(describeShape(shape))
        println("Area: ${shape.area()}")
    }
}

 

 

좀 다른 얘기긴 해도..

개인적으로 enum 코드를 주로사용하지만 when 에서 강제하기 위해서는 sealed 한정자를 써봐야겠다.

728x90

댓글