반응형
문제 상황
제가 느낀 문제는 다음과 같았습니다:
- 서로 다른 도메인이 같은 컬렉션 타입을 사용하고 있어, 구분하기 어렵다
- 같은 컬렉션 타입의 비즈니스 로직들이 서비스 레이어에 흩어져 있어서 관리가 어렵고 직관적으로 이해하기 어렵다
예를 들면, 두 개의 도메인이 같은 Map<Type, Boolean>
(적절한 예시인지는 모르겠지만..) 타입을 사용하고 있었던 겁니다.
서비스 레이어에서 이 타입의 변수를 만들고 수정하는 작업을 하는데, 이게 어떤 도메인과 관련된 코드인지 구분하기가 어려웠어요.
심지어 제가 직접 작성한 코드인데도 다시 보면 이해하는데 시간이 걸렸기에..
나도 보기 힘든데, 다른 사람은 더 힘들겠다는 생각에 개선하기로 마음먹었습니다..!
개선 방법
일급 컬렉션 도입
제가 시도한 개선은 일급 컬렉션을 도입하는 것이었습니다. 같은 타입을 사용하던 두 도메인을 각각의 일급 컬렉션으로 분리했습니다:
아래 코드는 예시 코드입니다. 어색한 부분이 있을 수 있겠지만, 감안하고 봐주세요!
// AS-IS: 단순한 컬렉션 사용
val studentScores: Map<String, Int> = mapOf(
"math" to 90,
"science" to 85,
"english" to 95
)
val courseGrades: Map<String, Int> = mapOf(
"math" to 90,
"science" to 85,
"english" to 95
)
// TO-BE: 각 도메인별 일급 컬렉션 사용
class StudentScores private constructor(
private val scores: Map<String, Int>,
) {
fun getAverage(): Double {
return scores.values.average()
}
fun getHighestScore(): Int {
return scores.values.maxOrNull() ?: 0
}
fun getSubjectScore(subject: String): Int {
return scores[subject] ?: 0
}
companion object {
fun from(scores: Map<String, Int>): StudentScores {
return StudentScores(scores)
}
}
}
class CourseGrades private constructor(
private val grades: Map<String, Int>,
) {
fun calculateFinalGrade(): String {
val average = grades.values.average()
return when {
average >= 90 -> "A"
average >= 80 -> "B"
average >= 70 -> "C"
else -> "D"
}
}
fun getPassingSubjects(): List<String> {
return grades.filter { it.value >= 70 }.keys.toList()
}
companion object {
fun from(grades: Map<String, Int>): CourseGrades {
return CourseGrades(grades)
}
}
}
이렇게 일급 컬렉션을 도입함으로써:
- 각 도메인이 독립적인 클래스로 관리되어, 각 클래스에 적절한 책임을 위임할 수 있게 되었습니다.
- 코드의 의도가 더 명확해져서 다른 개발자들도 이해하기 쉬워졌습니다. (나의 바람)
개선 결과
개선을 통해 다음과 같은 효과를 얻을 수 있었습니다:
- 각 도메인이 독립적인 클래스로 분리되어 명확한 책임을 갖게 했습니다.
- 관련된 로직이 클래스 내부에 위임되어 관리를 용이하게 했습니다.
- private 생성자와 정적 팩토리 메소드를 통해 객체 생성 과정을 제어.
- 저의 경우, 객체를 생성할 때 선행되어야 할 작업이 있어서 정적 팩토리 메소드만 사용해서 객체를 생성할 수 있도록 했습니다..!
'개발 > 리팩토링' 카테고리의 다른 글
엔티티 조회 대신 존재 여부만 확인하기 (0) | 2025.03.07 |
---|