타입 상수 재정의와 확장 방법

타입 상수를 재정의하고 확장하기 위한 두 가지 접근 방법에 대한 설명.

요약

  • Kotlin에서 타입 상수를 재정의하거나 변경하기 위한 유연한 구조 필요성.
  • 두 가지 접근법: interfaceopen class를 활용한 방법.
  • 각 방법의 구현 및 사용 예시 제공.

배경/문제

Kotlin에서 타입 상수를 재정의하거나 확장 프로젝트에서 동작을 변경할 때 구성의 유연성이 필요합니다. 특히 object는 불변(싱글톤) 형태이기 때문에 다른 프로젝트에서 이를 직접 변경하거나 상수를 재정의하기 어려운 문제가 발생합니다.

접근/해결 전략

이 문제를 해결하기 위해 두 가지 접근법을 제안합니다:

  1. interfacecompanion object를 활용하여 기본 구현을 제공하는 방법.
  2. open class를 사용해서 상속이 가능한 클래스를 정의하는 방법.

구현 포인트

1. interfacecompanion object 활용

기본 타입 정의를 interface에서 제공하고, 이를 상속하여 타입 값을 재정의합니다.

package axis.framework.core.util.type

interface UserTypeInterface {
    val MANAGER: String
    val MEMBER: String
    val ADMIN: String
    val SUPER_ADMIN: String
    val PROFESSOR: String
    val STAFF: String
    val STUDENT: String
    val ASSISTANT: String
    val COMPANY: String
    val COMPANY_SUB: String
    val NO_TYPE: String
}

object DefaultUserType : UserTypeInterface {
    override val MANAGER = "manager"
    override val MEMBER = "member"
    override val ADMIN = "admin"
    override val SUPER_ADMIN = "superAdmin"
    override val PROFESSOR = "professor"
    override val STAFF = "staff"
    override val STUDENT = "student"
    override val ASSISTANT = "assistant"
    override val COMPANY = "company"
    override val COMPANY_SUB = "company_sub"
    override val NO_TYPE = "noType"
}

class UserType(private val typeImpl: UserTypeInterface = DefaultUserType) {
    fun getRepresentType(typeString: String): String {
        return when {
            isAdminType(typeString) -> typeImpl.MANAGER
            isMemberType(typeString) -> typeImpl.MEMBER
            typeString == "common" -> typeString
            else -> ""
        }
    }

    fun isAdminType(typeString: String): Boolean {
        return typeString.equals(typeImpl.ADMIN, ignoreCase = false) ||
               typeString.equals(typeImpl.SUPER_ADMIN, false)
    }

    fun isMemberType(typeString: String): Boolean {
        return typeString.equals(typeImpl.PROFESSOR, false) ||
               typeString.equals(typeImpl.STAFF, false) ||
               typeString.equals(typeImpl.STUDENT, false) ||
               typeString.equals(typeImpl.ASSISTANT, false) ||
               typeString.equals(typeImpl.COMPANY, false) ||
               typeString.equals(typeImpl.COMPANY_SUB, false)
    }
}

다른 프로젝트에서 확장

object CustomUserType : UserTypeInterface {
    override val MANAGER = "otherManager"
    override val MEMBER = "otherMember"
    override val ADMIN = "otherAdmin"
    override val SUPER_ADMIN = "superAdminCustom"
    override val PROFESSOR = "otherProfessor"
    override val STAFF = "otherStaff"
    override val STUDENT = "otherStudent"
    override val ASSISTANT = "otherAssistant"
    override val COMPANY = "otherCompany"
    override val COMPANY_SUB = "otherCompanySub"
    override val NO_TYPE = "noTypeOther"
}

유틸 클래스 사용 예시

val customUserType = UserType(CustomUserType)
println(customUserType.getRepresentType("otherProfessor"))  // 출력: otherMember

2. 상속 가능한 클래스 사용

open class를 활용하여 기본 타입을 상속하고, 필요에 따라 재정의합니다.

수정된 기본 클래스

package axis.framework.core.util.type

open class UserType {
    open val MANAGER = "manager"
    open val MEMBER = "member"
    open val ADMIN = "admin"
    open val SUPER_ADMIN = "superAdmin"
    open val PROFESSOR = "professor"
    open val STAFF = "staff"
    open val STUDENT = "student"
    open val ASSISTANT = "assistant"
    open val COMPANY = "company"
    open val COMPANY_SUB = "company_sub"
    open val NO_TYPE = "noType"

    open fun getRepresentType(typeString: String): String {
        return when {
            isAdminType(typeString) -> MANAGER
            isMemberType(typeString) -> MEMBER
            typeString == "common" -> typeString
            else -> ""
        }
    }

    open fun isAdminType(typeString: String): Boolean {
        return typeString.equals(ADMIN, ignoreCase = false) ||
               typeString.equals(SUPER_ADMIN, false)
    }

    open fun isMemberType(typeString: String): Boolean {
        return typeString.equals(PROFESSOR, false) ||
               typeString.equals(STAFF, false) ||
               typeString.equals(STUDENT, false) ||
               typeString.equals(ASSISTANT, false) ||
               typeString.equals(COMPANY, false) ||
               typeString.equals(COMPANY_SUB, false)
    }
}

다른 프로젝트에서 상속하여 사용

class CustomUserType : UserType() {
    override val PROFESSOR = "otherProfessor"
    override val MEMBER = "otherMember"
}

유틸 클래스 사용 예시

val userType = CustomUserType()
println(userType.getRepresentType("otherProfessor"))  // 출력: otherMember

주의사항/트레이드오프

  • 구현의 복잡성: 인터페이스 기반 접근은 의존성과 구현을 분리하여 유지보수성을 높이지만, 구현해야 할 코드가 증가합니다.
  • 성능: 상속 기반 접근에서 매번 메소드를 오버라이드하는 경우 성능에 미치는 영향이 있을 수 있으니, 최적화를 고려해야 합니다.

마무리

  1. 인터페이스 기반 방식은 유연성과 재사용성이 뛰어나며, 여러 타입 정의를 쉽게 관리할 수 있습니다.
  2. 상속 기반 방식은 간단한 타입 변경이 필요할 때 유용합니다.

필요에 따라 두 가지 접근법 중 적합한 방법을 선택하여 구현하세요!


© 2024. Chiptune93 All rights reserved.