이번 2주차의 요구사항은 아래와 같다.
- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.
- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.
- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.
- 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
- 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
- 사용자가 잘못된 값을 입력할 경우
IllegalArgumentException을 발생시킨 후 애플리케이션은 종료되어야 한다.
- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다.
- 3항 연산자를 쓰지 않는다.
- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
- JUnit 5와 AssertJ를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 테스트 코드로 확인한다.
위 요구사항들을 구현하기 위해 아래와 같은 기능목록들을 만들었다.
InputManager Class
- 자동차 이름들을 입력 받는다.
- 자동차 이름 입력이 아래 조건을 지키는지 검사, 조건을 지키지 않으면 IllegalArgumentException 발생
- 시도 할 횟수를 입력 받는다.
- 입력한 시도 횟수가 아래 조건을 지키는지 검사, 조건을 지키지 않으면 IllegalArgumentException 발생
OutputManager Class
- 게임을 진행하는데 필요한 메시지 출력
- 각 횟수별 자동차의 전진 단계 출력
- 최종 우승자 출력
Car Class
- 이름을 파라미터로 받는 생성자
- 필드값으로, 이름, 이동거리 보유
- 이동거리 증가
- 각 필드에 대한 값 반환
- 이름 리스트를 입력받아 해당 이름을 가진 car 객체 리스트 반환
GameManager Class
- 0~9사이 무작위 값 생성
- 생성한 무작위 값이 4 이상인지 검사
- 값이 4 이상이면 해당 자동차의 이동 거리 증가
- 입력한 횟수를 진행 한 뒤 최종 우승자 산출
GameRunner Class
- 위 기능들을 조합하여 전체적 게임을 실행
폴더 구조
racingcar
├── game
│ └── GameManager
│ └── GameRunner
│ └── Car
├── io
│ ├── InputManager
│ └── OutputManager
│ └── CommonMessages
├── Application
└── Runner
1주차에는 공통적으로 출력하는 메시지를 properties 파일로 관리하였다. properties를 통해 한 파일 안에서 관리한다는 점은 좋았지만 동적으로 변화하는 메시지를 관리하기에는 한계가 있었다.
이번 2주차에는 출력이 로직에 따라 달라지기에 Enum을 통해 출력 메시지들을 관리 해 보았다.
public enum CommonMessages {
READ_NAMES("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"),
READ_TRIAL("시도할 회수는 몇회인가요?"),
GAME_RESULT("실행 결과");
private String message;
CommonMessages(String message) {
this.message = message;
}
public static String roundResult(List<Car> carList) {
StringBuilder sb = new StringBuilder();
carList.stream().forEach(car -> {
sb.append(car.getName() + " : " + car.getDistance() + "\n");
});
return sb.toString();
}
public static String printWinner(List<String> winnerList) {
StringBuilder sb = new StringBuilder();
sb.append("최종 우승자 : ");
int size = winnerList.size();
for(int idx = 0; idx < size; idx++) {
if(idx == size - 1) {
sb.append(winnerList.get(idx));
}else {
sb.append(winnerList.get(idx)).append(", ");
}
}
return sb.toString();
}
public String getMessage() {
return this.message;
}
}
이번 2주차의 피드백은 아래와 같다.
- 기능 목록을 재검토 한다.
- 기능 목록을 업데이트한다.
- 값을 하드 코딩하지 않는다.
- 함수가 한 가지 기능을 하는지 확인하는 기준을 세운다.
- 테스트를 작성하는 이유에 대해 본인의 경험을 토대로 정리해본다.
- 처음부터 큰 단위의 테스트를 만들지 않는다.
이번주차부터는 테스트 코드에 대한 중요성을 강조하고 있는것같다.
나의 개인적인 의견은 물론 테스트 코드는 중요하지만 모든 로직에 대하여 테스트케이스를 작성하고 나아가 구현 코드가 아닌 테스트 코드를 먼저 작성하는 TDD 개발 방법에 대해서는 회의적인 입장이다. 그러나 내가 아직 제대로 TDD 개발 방식에 무지하여 이렇게 생각하고 있는것일 수 있기에 이번주차에는 TDD 개발 방법에 대하여 학습해보았다.