Open
Conversation
- 길이 검증 구현 - 숫자 범위 검증 구현
- baseball 패키지: 야구 게임 도메인 로직 - framework 패키지: 재사용 가능한 게임 프레임워크 - utils 패키지: 유틸리티 클래스
- BaseballNumberGenerator 인터페이스 및 랜덤 구현체 추가 - BaseballGameService: 게임 로직 처리 - BaseballGameConsoleView: 콘솔 출력 처리 - BaseballGameTurnInput/Result: 턴 기반 게임 입출력 모델
- 자릿수(length), 숫자 범위(minRange, maxRange)를 중앙에서 관리 - 기본값 제공 및 커스터마이징 지원 - 설정 유효성 검증 로직 포함
- Config 기반으로 게임 시스템을 생성하는 팩토리 메서드 제공 - 기본 설정 및 커스텀 설정 지원
- BaseballNumber 패키지 경로 변경 반영
- main 메서드에서 GameFactory로 게임 시스템 생성 및 실행
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
느낀 점
이번 미션은 난이도 자체는 어렵지 않았다.
하지만 구현보다 설계에서 훨씬 많은 시간을 쓰게 된 문제였다.
처음에는 평소처럼 빠르게 기능부터 만들었다.
숫자를 입력받고, 스트라이크와 볼을 계산하고, 맞히면 종료.
30분 정도면 동작하는 프로그램은 충분히 만들 수 있었다.
문제는 “이걸 객체지향적으로 다시 만들려면 어디서부터 나눠야 하지?”라는 질문이었다.
단순히 클래스를 분리하는 건 어렵지 않았다.
Controller, Service, View 정도로 나누면 그럴듯한 구조가 나온다.
그런데 그렇게 나누고 나니 이상한 점이 보였다.
입출력이 바뀌면 Game 코드가 바뀌고,
게임 규칙이 바뀌면 실행 흐름도 바뀌었다.
겉으로는 나뉘어 있는데, 실제로는 서로 묶여 있었다.
따라서 “무엇이 바뀔 수 있는가?" 라는 질문을 기준으로 다시 나누기 시작했다.
Game과 GameSystem
숫자야구를 조금 떨어져서 보니
게임 자체와 게임을 실행하는 방식은 다른 개념이었다.
게임은 규칙이다.
하지만 재시작 여부, 실행 흐름, 인증 같은 건 규칙이 아니다.
그래서 둘을 분리했다.
이렇게 나누자 구조가 단순해졌다.
게임은 “플레이만” 알고
시스템은 “언제 다시 할지”만 안다.
재시작 기능을 넣어도 Game은 수정되지 않았고
비밀번호 등의 새로운 기능을 추가할지라도 게임 로직은 그대로였다.
이에 따라 관심사 분리를 통해 적절한 객체지향을 구현했다.
턴제 게임이라는 추상화
숫자야구의 게임 진행 패턴은 다음과 같다.
입력을 받는다 → 결과를 계산한다 → 맞출 때까지 반복한다
이건 숫자야구만의 규칙이 아니었다.
행맨등의 만들지라도 모두 같은 흐름을 가진다.
그래서 “숫자야구”를 만들지 않고
“게임 한 턴을 반복하는 구조”를 먼저 만들었다.
그리고 숫자야구는 그 안의 규칙만 구현하도록 바꿨다.
이렇게 하고 나니 게임을 새로 만드는 게 아니라 규칙만 교체하는 느낌이 들었다.
또한 턴제 게임이 아닌 실시간성을 반영한 게임도 인터페이스만 구현하면 끼워넣을 수 있었다.
테스트를 어렵게 만드는 것
구현하면서 가장 신경 쓴 부분은 랜덤과 콘솔 입력이었다.
처음엔 큰 문제라고 생각하지 않았다.
하지만 테스트를 해보려고 하자 바로 막혔다.
매번 다른 값이 나오고
입력을 넣어줘야만 동작했다.
그래서 둘 다 밖으로 밀어냈다.
정답 생성은 Generator로 분리하고
입력은 View에서만 처리하도록 했다.
그러자 Service는 순수 계산만 남았고 “테스트 가능한 코드”로 수정할 수 있었다.
구현보다 오래 걸린 부분
기능 구현 자체는 오래 걸리지 않았다.
시간 대부분은 어디까지를 도메인으로 볼 것인지 고민하는 데 사용했다.
특히 아래 기준을 계속 생각했다.
이 기준으로 옮기다 보니 클래스가 자연스럽게 나뉘었다.
설계를 미리 그렸다기보다
책임을 옮기다 보니 구조가 만들어진 느낌에 가까웠다.
마무리
이번 미션은 알고리즘 문제가 아니었다.
구현도 어렵지 않았다.
“이 코드는 어디에 있어야 하는가?”를 생각하게 만든 문제였다.
클래스를 늘리는 것이 객체지향이 아니라
변경 이유를 분리하는 것이라는 걸 다시 체감했다.
다음 미션에서는 기능이 더 많아질 텐데
이번에 세운 기준이 무너지지 않는지 확인해보고 싶다.