목차
- ✏️ 2주차 문제
- 🤔 문제를 풀며 고민해본 내용들
- ✨ 보기 좋은 코드가 먹기도(?) 좋다
✏️ 2주차 문제
2주차 미션은 숫자야구 게임입니다.
지난 1주차 미션과 다른 점은 알고리즘 문제가 아닌, 실제로 어떤 큰 기능을 구현해야 한다는 점입니다.
문제를 요약하자면 아래와 같습니다.
- 플레이어와 컴퓨터가 존재합니다.
- 컴퓨터는 1부터 9까지의 범위 내에서 랜덤으로 서로 다른 숫자 3개를 만듭니다. ex) 198
- 플레이어는 컴퓨터의 숫자를 알지 못합니다.
- 플레이어는 값을 제시해가며 컴퓨터의 숫자를 맞춥니다.
처음에 문제를 접했을 때에는 '어..? 할만 하겠는데?' 라고 생각하였습니다.
하지만 실제로 구현하는 과정에서 이러한 생각은 큰 오산이었다는걸 깨달았습니다.
문제 자체는 간단해보였지만, 각종 예외처리와 클린 코드를 고려하다보니 여간 쉬운 일이 아니었습니다.
🤔 문제를 풀며 고민해본 내용들
- 어떻게 하면 중복 코드를 줄일 수 있을까?
- 어떻게 하면 가독성을 높일 수 있을까?
- SOLID 원칙을 지키는 코드를 작성해보자
- 객체지향적인 설계란 무엇일까?
어떻게 하면 중복 코드를 줄일 수 있을까?
맨 처음 코드를 작성했을 때에는 Player와 Computer를 별도의 클래스 파일을 생성하여 구현하였습니다.
하지만 코드를 작성하다보니 중복되는 부분을 발견하였습니다.
우선 Player와 Computer의 공통 기능을 Role이라는 Super 클래스로 함께 가져가자 라는 생각을 하였습니다.
abstract class Role(
private val numbers: List<Int> = listOf()
) {
fun find(index: Int): Int = numbers[index]
fun contains(number: Int): Boolean = numbers.contains(number)
fun isEquals(index: Int, number: Int): Boolean = (find(index) == number)
}
package domain
class Player(
private val numbers: List<Int> = listOf()
) : Role()
// 검증 코드 생략
package domain
class Computer(
private val numbers: List<Int> = listOf()
) : Role()
// 검증 코드 생략
하지만 막상 코드를 작성하고 보니, 이 두 클래스(Player, Computer)는 numbers 리스트를 가지는 일급 컬렉션이라는 점 외에는 각 클래스만의 고유 기능이 필요하지 않다고 판단하였습니다.
즉, 검증, 기능 등 Palyer와 Computer 도메인에 대한 비즈니스 로직이 동일하다고 판단하였습니다.
따라서 Player와 Computer를 Role sealed 클래스를 상속받도록 리팩토링하였습니다.
리팩토링 결과는 아래와 같습니다.
sealed class Role(
protected val numbers: List<Int>
) {
init {
validate()
}
private fun validate() {
ExceptionHandler.validateListLengthOrException(numbers, Constants.DIGIT_LENGTH)
ExceptionHandler.validateDuplicatedOrException(numbers)
}
fun find(index: Int): Int = numbers[index]
fun contains(number: Int): Boolean = numbers.contains(number)
fun isEquals(index: Int, number: Int): Boolean = (find(index) == number)
}
class Player(numbers: List<Int>) : Role(numbers)
class Computer(numbers: List<Int>) : Role(numbers)
여러개의 클래스 파일을 만들 필요 없이, 중복되는 기능을 통합하여 각 역할만 다르게 해주었습니다.
이렇게 코드를 작성하다보니 Player와 Computer 자체의 역할을 구분되고, 중복되는 코드를 피할 수 있었습니다.
어떻게 하면 가독성을 높일 수 있을까?
로직 구현, 함수 변수 네이밍, 패키지 구조 등 클린 코드와 가독성을 높이고자 고민을 끊임없이 하였습니다.
사실상 우테코에서 제공해주신 프로그래밍 요구사항만 잘 지키더라도 이러한 고민을 어느정도 해결할 수 있었습니다.
- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
해당 요구사항을 '절대' 지키기 위해 노력하다보니, 어느순간 자연스럽게 메서드는 기능별로 나뉘어져 있었습니다.
메서드가 기능별로 나뉘어져 있다보니 가독성 또한 물흐르듯 높아졌습니다.
SOLID 원칙을 지키는 코드를 작성해보자
기능을 하나로 뭉치지 않고 잘게 쪼개다보니, 메서드 규모에서 단일책임원칙(SRP) 또한 지키고 있었습니다.
클래스 또한 Client가 필자의 코드를 사용한다고 가정하고, 기능이 변경되어도 Client의 코드는 최대한 변경없이 사용할 수 있도록 구현해보고자 하였습니다.
동시에, 의존성 역전의 원칙(DIP)를 지키는 코드를 작성해보고자 BaseballGame(구현체)을 직접적으로 의존하지 않도록 Game이라는 인터페이스를 추가로 만들어주었습니다.
만약 BaseballGame이 아닌, BaseketballGame, SoccerGame이 확장된다고 하더라도, Client는 BaseballGame에 직접적으로 의존하지 않으므로, 수정을 최소화할 수 있습니다.
객체지향적인 설계란 무엇일까?
객체지향적인 설계에 대해 고민해보는 한 주 였습니다.
SOLID 원칙을 지킨다고 객체지향적인 설계일까?
객체지향 4가지 특징(추상화, 캡슐화, 상속, 다형성)을 지킨다고 객체지향적인 설계일까?
프로그래밍에 정답은 없다고 생각합니다.
각종 강의, 세미나, 영상 등을 참고해보아도 오히려 이러한 주제에 대해 조심스럽게 말씀하시는 경향을 많이 보았습니다.
다만, 제가 한 주간 느낀 객체지향(Oriented)의 목적은 아래와 같았습니다.
누구라도 사용하기 쉬워야 한다.
개발자가 프로그래밍에 대한 개념이 있다면, 그리고 객체지향적으로 설계하였다면 누구라도 코드를 쉽게 사용할 수 있도록 하는 것이 목적이라고 느꼈습니다.
누군가 내 코드를 사용할 때 어려움을 느낀다면, 아무리 설계에 대해 고민하였다 하더라도 무용지물이기 때문입니다.
물론 이 생각 또한 정답이라고 할 수는 없습니다.
그러나 최소한 본인이 생각하는 객체지향의 목적을 생각하면서 개발하는 것이 중요하다고 느꼈습니다.
✨ 보기 좋은 코드가 먹기도(?) 좋다
클린 코드를 준수하려고 노력하다보니, 가독성이 높아지는 것은 당연했고 유지보수성 또한 높아지는 듯 하였습니다.
주어진 문제의 규모가 크지 않기 때문에 유지보수성이 크게 중요하지 않다고 생각할 수도 있습니다.
하지만 이러한 습관이 들이다보면 규모가 크든, 작든 누구라도 코드를 쉽게 이해하고 사용할 수 있을 것이라는 생각이 들었습니다.
즉, 어떤 개발자라도 제 코드를 먹기 좋은 코드라고 느끼도록 만드는 것이 중요하다고 생각하였습니다.
앞으로 남은 프리코스 기간 동안 클린 코드와 객체지향에 대해 꾸준히 공부하고 적용해보고 싶습니다.
github : https://github.com/tmdgh1592
tmdgh1592 - Overview
느리더라도 천천히..!! 😁. tmdgh1592 has 21 repositories available. Follow their code on GitHub.
github.com
'대외활동 > 우아한테크코스' 카테고리의 다른 글
[우아한테크코스] 2주차 - TDD(Test Driven Development) (1) | 2023.02.15 |
---|---|
[우아한테크코스 5기] 1주차 회고 - (양념 반 후라이드 반) 설렘 반 걱정 반 (6) | 2023.02.13 |
[우아한테크코스 5기] 프리코스 4주차 회고 - Kotlin 안드로이드 (2) | 2022.11.23 |
[우아한테크코스 5기] 프리코스 3주차 회고 - Kotlin 안드로이드 (1) | 2022.11.23 |
[우아한테크코스 5기] 프리코스 1주차 회고 - Kotlin 안드로이드 (2) | 2022.11.06 |