관리 메뉴

나만을 위한 블로그

[이펙티브 코틀린] 아이템 32. 추상화 규약을 지켜라 본문

책/Effective Kotlin

[이펙티브 코틀린] 아이템 32. 추상화 규약을 지켜라

참깨빵위에참깨빵_ 2023. 1. 28. 01:04
728x90
반응형

규약은 개발자들의 단순한 합의다. 따라서 한쪽에서 규약을 위반할 수도 있다. 기술적으로 모든 부분에서 이런 규약 위반이 발생할 수 있다. 예를 들어 아래처럼 리플렉션을 활용하면 원하는 걸 열고 사용할 수 있다.

 

import kotlin.reflect.full.declaredMemberFunctions
import kotlin.reflect.jvm.isAccessible

class Employee {
    private val id: Int = 2
    override fun toString(): String = "User(id = $id)"

    private fun privateFunction() = println("Private function called")
}

fun callPrivateFunction(employee: Employee) {
    employee::class.declaredMemberFunctions
        .first { it.name == "privateFunction" }
        .apply { isAccessible = true }
        .call(employee)
}

fun changeEmployeeId(employee: Employee, newId: Int) {
    employee::class.java.getDeclaredField("id")
        .apply { isAccessible = true }
        .set(employee, newId)
}

fun main() {
    val employee = Employee()
    callPrivateFunction(employee)
    changeEmployeeId(employee, 1)
    print(employee)
}
// Private function called
// User(id = 1)

 

뭔가를 할 수 있다는 게 그걸 해도 괜찮다는 의미는 아니다. 현재 코드는 private 프로퍼티와 private 함수명과 같은 세부적인 정보에 매우 크게 의존하고 있다. 이런 이름은 규약이라고 할 수 없기 때문에 언제든 변경될 수 있다. 따라서 이런 코드를 프로젝트에서 쓴다면 프로젝트 안에 시한 폭탄을 설치한 것과 같다.

규약은 보증과 같다. 규약을 위반하면 코드가 작동을 멈췄을 때 문제가 된다.

 

상속된 규약

 

클래스를 상속하거나 다른 라이브러리의 인터페이스를 구현할 때는 규약을 반드시 지켜야 한다. 예를 들어 모든 클래스는 equals()와 hashCode()를 가진 Any 클래스를 상속받는다. 이런 메서드는 모두 우리가 반드시 존중해야 하고 지켜야 하는 규약을 갖고 있다.

만약 규약을 안 지킨다면 객체가 제대로 동작하지 않을 수 있다. 예를 들어 hashCode()가 제대로 구현되지 않으면 HashSet과 함께 사용할 때 제대로 동작하지 않는다. 아래 코드는 원래 set은 중복을 허용하지 않는데 equals()가 제대로 구현되지 않아 중복을 허용해 버린다.

 

class Id(val id: Int) {
    override fun equals(other: Any?): Boolean = other is Id && other.id == id
}

fun main() {
    val set = mutableSetOf(Id(1))
    set.add(Id(1))
    set.add(Id(1))
    println(set.size)
}
// 3

 

반응형
Comments