[내일배움캠프] 숫자 야구 게임 구현 - 리팩토링(2)

2024. 9. 21. 22:49내일배움캠프

 어제 Level03 요구사항을 반영하기 위해 작성한(커밋은 오늘 했지만...) 코드를 다시 한 번 확인하고 리팩토링 해보았다. 수정한 코드는 여기서 확인 가능한다.

 

1. 패키지명 수정

 현재 패키지 구성을 다시 확인하면서 일부 패키지의 이름이 의도를 명확히 전달하지 못하는 것 같아 수정하였다. 아래는 수정 전의 패키지 상태이다.

현재 패키지 상태

 

패키지명을 왜 수정하게 됬는지 설명하기 앞서 현재 패키지 구성에 대해 설명하자면, 현재의 패키지는 'package' 경로를 통해 해당 클래스(또는 인터페이스)가 어떤 목적으로 작성된 것인지 유추할 수 있도록 구성되었다. 예를 들어 이번에 추가한 'GameRecorder' 인터페이스의 패키지 경로는 아래와 같다.

package numberBaseball.domain.playRecord;

 

패키지 경로를 통해 'GameRecorder 인터페이스' 는 숫자야구(numbersBaseball)의 플레이 기록(playRecord) 집합(또는 영역, domain)에 포함(해당)되는 인터페이스임을 유추할 수 있다. 패키지 구성 자체가 클래스(or 인터페이스)를 파악하는데 도움되는 것이다. 하지만 현재 'correctAnswer', 'dateCompare', 'palyerInput', 'playRecord' 같은 패키지 명은 의도 전달이 명확하지 않아 이러한 패키지 구성의 장점을 살리지 못하고 있다 판단해 아래와 같이 해당 패키지들의 이름을 수정하였다.

수정된 패키지명

  • correctAnswer → generateCorrectAnswer : '정답 생성과 관련된 클래스 및 인터페이스의 집합' 이란 의미를 전달하기 위해 패키지명을 'generateCorrectAnswer' 로 수정했다.
  • dateCompare → checkCorrectAnswer : '정답 확인과 관련된 클래스 및 인터페이스의 집합' 이란 의미를 전달하기 위해 패키지명을 'checkCorrectAnswer' 로 수정했다.
  • playerInput → validateUserInput : '사용자 입력 검증과 관련된 클래스 및 인터페이스의 집합' 이란 의미를 전달하기 위해 패키지명을 'validateUserInput' 으로 수정했다.
  • playRecord → saveGameRecord : '게임 기록 저장과 관련된 클래스 및 인터페이스의 집합' 이란 의미를 전달하기 위해 패키지명을 'saveGameRecord' 로 수정했다.

이렇게 되서 패키지 경로를 통해 좀 더 명확하게 클래스의 역할을 유추할 수 있게 되었다.

 

 

2. 데이터 정확성 문제 해결

 사용자가 정답을 맞추게 되면 정답을 맞추기까지 시도한 횟수가 기록(저장)되며, 이후 시작 메뉴의 '2. 게임 기록 보기' 를 선택한 경우 지금까지 플레이한 게임에 대한 기록이 출력되어야 한다. 하지만 현재 '게임 기록' 을 한 번 조회하고 나서 다시 조회(새 게임 플레이 이후 또는 즉시)하면 이전에 확인한 기록이 출력되지 않는 문제를 발견했다. 문제 해결을 위해 원인을 찾아보니 아래와 같은 원인을 찾을 수 있었다.

GameRecorderImpl 클래스의 필드 변수들

 

원인은 'GameRecordImpl' 클래스에 있었다. 게임기록을 저장하고 쉽게 반환받고자 'Queue' 컬랙션을 사용했다. 해당 컬렉션의 요소를 꺼내기 위해서 'Queue.poll()' 메서드를 사용해야 하는데, 해당 메서드는 컬랙션 객체에서 요소를 꺼내고 객체에서 제거한다. 여기까지만 보면 "어? 그러면 필드에 직접적으로 접근한게 아니니 문제가 없지 않을까?" 라 생각할 수 있다. 하지만 게임 기록을 확인하는 과정에서 사용된 'GameRecorderImpl.getGameRecords()' 메서드를 보면 왜 'Queue.poll()' 메서드를 먼저 언급했는지 알 수 있다.

GameRecorderImpl.getGameRecords() 메서드

 

'getGameRecords()' 메서드는 'gameRecords' 를 그대로, 즉 객체 주소를 반환하기 때문에, 이를 참조(저장)한 변수에 'Queue.poll()' 메서드를 사용하면 자연스레 'GameRecorderImpl' 객체의 'gameRecords(원본 객체)' 에 영향(전파)이 가게 된다. 그래서 직접적으로 'GameRecorderImpl' 필드에 접근한 것은 아니지만 직접 접근했을 때와 같은 문제가 발생하며 '게임 기록' 이란 데이터를 훼손, '데이터의 정확성' 을 떨어뜨리게 된다.

 

해당 문제를 해결하기 위해서는 'gameRecords' 를 '얕은 복제(객체 주소 복사)' 를 해 반환할게 아니라 '깊은 복제(원본 객체와 같은 요소 및 순서를 가지는 동일 타입 객체 생성)' 를 해 반환해야 한다.

수정된 GameRecorderImpl 클래스

 

그래서 'gameRecords' 의 요소(게임 기록)들을 반환하는 메서드 'getGameRecords()' 의 경우 새로운 컬랙션 객체에 'gameRecords' 의 요소들을 담아 반환하도록 수정하였다.

 

그리고 필드 변수 'gameRecords' 의 경우 "Queue 컬랙션이 적절한 선택인가?" 라는 생각을 했을 때 납득할 만한 이유를 찾지 못 했는데, 사실 'gameRecords' 는 게임 기록의 순서(추가 순서)가 보장되어야 하기 때문에, 순서를 보장하는 컬랙션인 'List' 로 타입을 변경하였다.

 

 

3. 기타 수정 사항

 정답은 '1~9' 사이의 숫자로 구성되어야 한다. 하지만 구성 가능한 숫자 요소들을 갖는 상수 컬랙션 객체(numbers)에 '[0, 1, 2, 3, 4, ..., 9]' 이 저장되어 있어 '0' 이 정답 구성요소에 포함되어 버렸다. 그래서 아래와 같이 수정했다.

정답 구성 요소 리스트 numbers 수정

 

그리고 '추가 작성한 시스템 메시지' 를 필드로 갖는 상수를 SystemMessage(enum) 클래스에 아래와 같이 추가하였다. 

추가된 시스템 메시지를 필드로 갖는 상수들