모든 코드는 여기를 클릭 하시면 확인 하실 수 있습니다.
안녕하세요. TDD로 개발하기 마지막 파트입니다. 전체 포스팅은 전체 사이클을 경험하는 느낌으로 작성하였고 다음 포스팅 번외편에서 객체지향적인 방법을 추가하여 리팩토링을 해보겠습니다. 이번 포스팅에서는 현재 저희가 아직까지 하지 않았던 아래의 할 일 목록들을 해결해 보겠습니다. 참고로 이번 포스팅 부분은 TDD로 개발 된 도메인을 서로 연결하기 위한 과정임으로 TDD와는 거리가 멉니다. 단순히 프로그램 실행을 위해 연결 및 출력과 관련된 부분을 포스팅 할 예정입니다. (물론 출력은 테스트가 되지만 TDD로 할 필요는 없다고 생각해서 개발 후 단위 테스트를 조금 추가하겠습니다. )
public class Application {
public static void main(String[] args) {
new RacingGame().run();
}
}
,
를 기준으로 분리하고 각 사용자마다 자동차를 생성합니다. 이 과정은 for 문을 통해 입력 된 이름에 맞게 자동차를 Controller에서 생성해도 되지만 정적 팩토리 메서드의 형태로 작성하겠습니다. 현재로서는 굳이 사용해야 할 이유는 없지만 이름을 통해 의도를 명확히 드러내고 로직 자체가 Controller보단 따로 클래스로 분리하는 것이 확장성면에서 좋을 것 같아 분리하여서 진행하겠습니다. package kail.study.java.racing.domain;
import java.util.ArrayList;
import java.util.List;
public class CarFactory {
public static List<Car> create(List<String> names) {
List<Car> racingCars = new ArrayList<>();
for (String name : names) {
racingCars.add(new Car(name));
}
return racingCars;
}
}
CarFactory Class
를 통해 경주에 참여 할 자동차를 만들고 아래에서 플레이하고자 하는 횟수를 입력 받고 전체 게임을 진행하겠습니다. 아래의 코드는 전반적인 진행순서를 보여주는 Controller의 역할을 하니 아래의 코드를 보고 추가 된 코드를 보시면 이해에 도움이 될 것 같습니다. public class RacingGame {
private RacingCars racingCars;
private Round round;
public void run() {
initializeRace();
playGame();
}
private void initializeRace() {
try{
List<String> carNames = StringUtils.parseByComma(InputView.getInput());
racingCars = CarFactory.create(carNames);
round = new Round(InputView.getTimes());
} catch (Exception e) {
System.out.println(e.getMessage());
initializeRace();
}
}
private void playGame() {
while(!round.isEnd()){
racingCars.move();
OutputView.printRoundResult(racingCars.getCars());
round.reduce();
}
OutputView.printWinner(racingCars.getWinner());
}
}
isEnd()
메서드와 reduce()
메소드가 추가되었습니다. // Round Class
public boolean isEnd() {
return this.round == 0;
}
public void reduce() {
this.round--;
}
@Test
@DisplayName("reduce 메소드를 통해 라운드가 감소하는 지 테스트")
void reduce() {
Round round = new Round("2");
round.reduce();
round.reduce();
assertThat(round.isEnd()).isTrue();
}
@Test
@DisplayName("프로그램 오작동으로 라운드가 종료되었는데도, reduce를 호출하는 경우")
void isEnd() {
Round round = new Round("1");
round.reduce();
assertThatThrownBy(() -> {
round.reduce();
}).isInstanceOf(RuntimeException.class)
.hasMessageContaining("모든 라운드가 종료되었습니다.");
}
move()
라는 매소드와 우승자를 출력하기 위한 getWinner()
매서드를 추가하였습니다. 승리자는 각각의 차의 포지션에 의해 결정됨으로 최대 포지션을 구한 이후 각 차에게 최대 포지션인지 물어보는 메세지를 보내어 True
가 반환되는 경우 추가하도록 하였습니다. public void move() {
for (Car car : cars) {
car.move(RandomGenerator.create());
}
}
public List<Car> getWinner() {
List<Car> winner = new ArrayList<>();
for(int i =0; i<cars.size(); i++) {
if(cars.get(i).isWinner(getMaxPosition()))
winner.add(cars.get(i));
}
return winner;
}
private int getMaxPosition() {
Car max = Collections.max(cars);
return max.getPosition();
}
public List<Car> getCars() {
return cars;
}
max
값과 같은지를 리턴하는 isWinner()
매서드를 추가하였습니다. public class Car implements Comparable<Car> {
@Override
public int compareTo(Car anotherCar) {
return position - anotherCar.position;
}
public boolean isWinner(int maxPosition) {
return position == maxPosition;
}
}
package kail.study.java.racing.view;
import java.util.List;
import java.util.stream.Collectors;
import kail.study.java.racing.domain.Car;
public class OutputView {
public static void printRoundResult(List<Car> cars) {
System.out.println("실행 결과");
for(Car car : cars) {
System.out.println(car.getName() + " : " + car.getPosition());
}
}
public static void printWinner(List<Car> winner) {
String winnerNames = winner.stream()
.map(Car::getName)
.collect(Collectors.joining(", "));
System.out.println(String.format("우승자는 %s입니다",winnerNames));
}
}
우아한 테크코스를 진행하며 했던 미션을 블로그에 정리하면서 진행 해 보았는데요. 제가 짰던 코드와 유사하게 작성하였음에도 불구하고 글로 생각을 정리하는 일은 어려운 일인 것 같습니다. 호옥시나 누군가 보셨다면 유용하셨길 바라며 TDD 로 커멘드라인 개발하기 프로젝트는 여기서 마무리하겠습니다.
추가적으로 현재 코드에서는 리팩토링을 할 부분이 많습니다. 하지만 이 부분은 TDD보다는 객체지향에 가깝다고 생각이 들어 번외편으로 리팩토링 과정 부분을 추가 할 예정입니다. 객체지향적인 부분에서 부족한 점이 많기 때문에 추가적인 공부를 하면서 포스팅 하도록 하겠습니다.
와 엄청 도움이 되네요 하하