타입 상수 재정의와 확장 방법
in Kotlin / Development on Kotlin, 객체지향, 프로그래밍, 확장성
타입 상수를 재정의하고 확장하기 위한 두 가지 접근 방법에 대한 설명.
요약
- Kotlin에서 타입 상수를 재정의하거나 변경하기 위한 유연한 구조 필요성.
- 두 가지 접근법:
interface와open class를 활용한 방법. - 각 방법의 구현 및 사용 예시 제공.
배경/문제
Kotlin에서 타입 상수를 재정의하거나 확장 프로젝트에서 동작을 변경할 때 구성의 유연성이 필요합니다. 특히 object는 불변(싱글톤) 형태이기 때문에 다른 프로젝트에서 이를 직접 변경하거나 상수를 재정의하기 어려운 문제가 발생합니다.
접근/해결 전략
이 문제를 해결하기 위해 두 가지 접근법을 제안합니다:
interface와companion object를 활용하여 기본 구현을 제공하는 방법.open class를 사용해서 상속이 가능한 클래스를 정의하는 방법.
구현 포인트
1. interface와 companion 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
주의사항/트레이드오프
- 구현의 복잡성: 인터페이스 기반 접근은 의존성과 구현을 분리하여 유지보수성을 높이지만, 구현해야 할 코드가 증가합니다.
- 성능: 상속 기반 접근에서 매번 메소드를 오버라이드하는 경우 성능에 미치는 영향이 있을 수 있으니, 최적화를 고려해야 합니다.
마무리
- 인터페이스 기반 방식은 유연성과 재사용성이 뛰어나며, 여러 타입 정의를 쉽게 관리할 수 있습니다.
- 상속 기반 방식은 간단한 타입 변경이 필요할 때 유용합니다.
필요에 따라 두 가지 접근법 중 적합한 방법을 선택하여 구현하세요!