자동차 경주 게임
기능 요구사항
- 각 자동차에 이름을 부여할 수 있다. 자동차 이름은 5자를 초과할 수 없다.
- 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- 자동차 이름은 쉼표(,)를 기준으로 구분한다.
- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4이상일 경우이다.
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한명 이상일 수 있다.
실행 결과
- 위 요구사항에 따라 3대의 자동차가 5번 움직였을 경우 프로그램을 실행한 결과는 다음과 같다.
경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).
pobi,crong,honux
시도할 회수는 몇회인가요?
5
실행 결과
pobi : -
crong : -
honux : -
pobi : --
crong : -
honux : --
pobi : ---
crong : --
honux : ---
pobi : ----
crong : ---
honux : ----
pobi : -----
crong : ----
honux : -----
pobi : -----
crong : ----
honux : -----
pobi, honux가 최종 우승했습니다.
힌트
- 자동차는 자동차 이름과 위치 정보를 가지는 Car 객체를 추가해 구현한다.
프로그래밍 요구사항
- 자바 코드 컨벤션을 지키면서 프로그래밍한다.
- 기본적으로 Google Java Style Guide을 원칙으로 한다.
- 단, 들여쓰기는 '2 spaces'가 아닌 '4 spaces'로 한다.
- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
- else 예약어를 쓰지 않는다.
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
- 3항 연산자를 쓰지 않는다.
- 함수(또는 메소드)가 한 가지 일만 하도록 최대한 작게 만들어라.
- 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외
- 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.
- UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다.
- 모든 원시 값과 문자열을 포장한다.
- 일급 컬렉션을 쓴다.
이번 과제에서 중요했던 점은 3가지로 볼 수 있다
- TDD 하기 어려운 메소드 의존 관계 축소
- 모든 원시값과 문자열을 포장
- 일급콜렉션 사용
1. TDD 하기 어려운 메소드 의존 관계 축소
저번 과제에서도 그렇고 이번 과제에서도 테스트를 어떻게 해야될지 모르겠는부분이 랜덤 숫자를 구하는 부분이었다
랜덤 숫자를 구하는 메소드 자체는 Java 내에서 제공하는 함수를 사용하기 때문에 테스트가 필요하지 않지만
문제는 다른 메소드들이 모두 랜덤 숫자 구하는 메소드에 연결되어있는게 문제였다
예를 들면
자동차가 이동할지 말지 여부를 결정하는 메소드 move()와
랜덤 숫자를 구하는 getRandomNumber() 메소드가 있다면 move()를 테스트하기가 어려워 진다
private static final int STANDARD_NUMBER = 4;
public void move(){
int randomNumber = getRandomNumber();
if(randomNumber >= STANDARD_NUMBER){
car.addMove();
}
}
public int getRandomNumber(){
return (int)(Math.random() * (MAX_NUMBER - MIN_NUMBER)) + MIN_NUMBER;
}
이런식으로 다른 Object와 의존관계를 갖는 노드들을 쭉 그려나가는 것을 Object Graph 라고 한다
따라서 테스트를 진행할 경우 이러한 Object Graph에서 의존관계를 갖지 않는 가장 작은 단위의 노드를 찾아서 테스트가 가능하도록 구조를 변경해야한다
아래 그림처럼 테스트하기 어려운 코드의 의존관계를 Object Graph 상위 노드로 이동시킨다
2. 모든 원시값과 문자열을 포장
원시값과 문자열을 객체로 포장하게되면 유효성 체크나 값을 쉽게 변경하지 못하도록 할 수 있는 등의 이점이 있다
리팩토링 시에도 객체로 분리되어있으면 메소드를 추가하거나 변경할 때 고통을 덜 받을 수 있다
public class Car {
private final String name;
private int point;
...
...
...
...
}
public class Car {
private final Name name;
private final Point point;
...
...
...
...
}
위의 코드를 아래와 같이 변경할 경우 우선 point 변수만 보더라도 final로 선언이 가능해진다
이렇게 객체화 하게 되면 도메인 로직을 짜는 중에도 관련 메소드를 해당 객체에 구현하도록 설계가 되는 것을 경험할 수 있었다
3. 일급콜렉션 사용
List나 Map 형태 등의 변수를 선언했을 경우 무분별하게 데이터를 추가하거나 삭제, 변경을 할 수 없도록 일급 콜렉션을 사용하여 막을 수 있다
public class RacingGame {
private final List<Car> cars;
...
...
...
}
public class RacingGame {
private final Cars cars;
...
...
...
}
마무리 느낌점
짧은 시간에 많은 내용을 지키려해서 그런지 더 잘 안되는 것 같다..ㅜㅜ
의존 관계를 줄이기 위해서 랜덤 함수는 분리하였는데 하다보니 또 다른 부분에서 의존 관계가 굉장히 굉장히~~ 많이 만들어지고 있었다ㅋㅋㅋㅋㅋ
결국 대부분의 메소드는 TDD를 하기 어렵게 되었고 다른 요구사항에 집중해보았다
겨우 CAR 객체 안에 있는 name 값과 point 값 2개의 원시값과 문자열을 객체로 변경했을 뿐인데 리팩토링에 굉장히 애를 먹었다
내가 평소에 getter, setter에 많이 의존했다는 것도 깨달을 수 있었고 객체로 변경하다보니 메소드 분리가 좀 더 쉽게 이루어지긴했으나 또 의존 관계에 부딧쳐서 코드가 엉망이 되었다ㅜㅜ
이부분은 과제를 하면서 연습을 하고 다시 리팩토링을 시도해야될 것 같다
너무 만족스럽지 못한 코드가 나왔다 흑흑
최악 수준
슬슬 코드양이 많아져서 코드는 git 주소로 대체
'개발 교육 > 플레이그라운드 with TDD, 클린코드' 카테고리의 다른 글
Factory Method 실습 - 연료 주입 (0) | 2022.03.16 |
---|---|
자동차 경주 게임 구현 - 리팩토링 재시도 (0) | 2022.03.14 |
TDD/리팩토링 실습 - 문자열 덧셈 계산기 (0) | 2022.03.09 |
숫자야구게임 다시 구현 - 피드백 적용 후 (0) | 2022.03.06 |
숫자야구게임 구현 - 피드백 적용 전 (0) | 2022.03.06 |