기능 요구사항
- 각 자동차에 이름을 부여할 수 있다. 자동차 이름은 5자를 초과할 수 없다.
- 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.
- 자동차 이름은 쉼표(,)를 기준으로 구분한다.
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한명 이상일 수 있다.
실행 결과
- 위 요구사항에 따라 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가 최종 우승했습니다.
힌트
- 규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
- indent가 2이상인 메소드의 경우 메소드를 분리하면 indent를 줄일 수 있다.
- else를 사용하지 않아 indent를 줄일 수 있다.
- 자동차 이름을 쉼표(,)를 기준으로 분리하려면 String 클래스의 split(",") 메소드를 활용한다.
String[] names = inputName.split(",");
- 사용자가 입력한 이름의 숫자 만큼 자동차 대수를 생성한다.
- 자동차는 자동차 이름과 위치 정보를 가지는 Car 객체를 추가해 구현한다.
프로그래밍 요구사항
- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.
- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.
- 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.
- 모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외
- 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.
- UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다.
- 자바 코드 컨벤션을 지키면서 프로그래밍한다.
- else 예약어를 쓰지 않는다.
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.
기능 목록 및 commit 로그 요구사항
- 기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리해 추가한다.
- git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다.
코드 작성
package step4;
public class Car {
private int position;
private String name;
public Car(int position, String name) {
this.position = position;
this.name = name;
}
public void moveForward() {
this.position++;
}
public int getPosition() {
return position;
}
public String getName() {
return name;
}
}
package step4;
public class CarManager {
private Car car;
private RaceManager raceManager = new RaceManager();
public CarManager(Car car) {
this.car = car;
}
public void canIGo() {
if (raceManager.judge()) {
car.moveForward();
}
}
public int getCurrCarPosition() {
return car.getPosition();
}
public String getCurrCarName() {
return car.getName();
}
}
package step4;
import java.util.Scanner;
public class InputView {
private static final String HOW_MANY_CAR = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).";
private static final String HOW_MANY_CYCLE = "시도할 횟수는 몇 회 인가요?";
private static final int MIN_ROUND = 0;
private static final int MIN_CAR = 0;
private static final String CYCLE_ERROR_MESSAGE = "시도할 횟수는 0보다 커야 합니다.";
private static final String CAR_ERROR_MESSAGE = "자동차 대수는 1대 이상이어야 합니다.";
private static final Scanner sc = new Scanner(System.in);
public int getNumOfCycle() {
System.out.println(HOW_MANY_CYCLE);
return getInput();
}
public String getNameOfCar() {
System.out.println(HOW_MANY_CAR);
return getStringInput();
}
private int getInput() {
return sc.nextInt();
}
private String getStringInput() {
return sc.next();
}
public void inputCarCheck(int numOfCar) {
System.out.println(numOfCar);
if (numOfCar <= MIN_CAR) {
throw new IllegalArgumentException(CAR_ERROR_MESSAGE);
}
}
public void inputRoundCheck(int numOfCycle) {
if (numOfCycle <= MIN_ROUND) {
throw new IllegalArgumentException(CYCLE_ERROR_MESSAGE);
}
}
}
package step4;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Race {
private static int numOfCar;
private static int numOfCycle;
private static String nameOfCar;
private static final int INIT_CAR_POSITION = 1;
private InputView inputView = new InputView();
private ResultView resultView = new ResultView();
private List<CarManager> carManagers;
public Race(String nameOfCar, int numOfCycle) {
this.nameOfCar = nameOfCar;
this.numOfCar = getNumOfCar();
this.numOfCycle = numOfCycle;
validInputConfirm();
prepareCars();
}
public int getNumOfCar() {
return nameOfCar.split(",").length;
}
public void prepareCars() {
String[] carNamesAry = nameOfCar.split(",");
carManagers = new ArrayList<>();
for (int i = 0; i < numOfCar; i++) {
System.out.println(carNamesAry[i]);
Car car = new Car(INIT_CAR_POSITION,carNamesAry[i]);
carManagers.add(new CarManager(car));
}
}
public void validInputConfirm() {
inputView.inputCarCheck(numOfCar);
inputView.inputRoundCheck(numOfCycle);
}
public void playRace() {
for (CarManager carManager: carManagers) {
carManager.canIGo();
}
}
public void printResult() {
for (CarManager carManager: carManagers) {
resultView.showCurrCarName(carManager.getCurrCarName());
resultView.showCurrCarPosition(carManager.getCurrCarPosition());
resultView.findMaxPositionCarName(carManager.getCurrCarName(), carManager.getCurrCarPosition());
}
}
}
package step4;
import java.util.Random;
import java.util.Scanner;
public class RaceManager {
private static final int DICE_THRETHOLD = 4;
private static Random rd = new Random();
public boolean judge() {
int rdNumber = rd.nextInt(10);
if (rdNumber >= 4) {
return true;
}
return false;
}
}
package step4;
import step3.model.Car;
public class ResultView {
private static String maxCarName;
private static int maxCarPosition = 0;
public void showCurrCarPosition(int location) {
for (int i = 0; i < location; i++) {
System.out.print("-");
}
System.out.println();
}
public void showCurrCarName(String name) {
System.out.print(name+": ");
}
public void findMaxPositionCarName(String carName, int carPosition) {
if (carPosition > maxCarPosition) {
maxCarName = carName;
maxCarPosition = carPosition;
} else if (carPosition == maxCarPosition) {
maxCarName += ", "+carName;
}
}
public void printFinalResult() {
System.out.println(maxCarName+"가 최종 우승했습니다.");
}
}
import step4.InputView;
import step4.Race;
import step4.ResultView;
import java.util.Scanner;
public class RacingCarMain {
public static void main(String[] args) {
InputView inputView = new InputView();
String nameOfCar = inputView.getNameOfCar();
int numOfCycle = inputView.getNumOfCycle();
Race race = new Race(nameOfCar, numOfCycle);
for (int i = 0; i < numOfCycle; i++) {
race.printResult();
race.playRace();
System.out.println();
}
race.printResult();
ResultView resultView = new ResultView();
resultView.printFinalResult();
}
}
unit test를 하는 것이 아직 어색하다.
추가적으로 모든 코드를 다 작성하고 모듈이 돌아가는 것을 확인한 뒤에 테스트 코드를 짜려고 하니 '이걸 왜 하는거지? 이미 코드는 잘 돌아가는데?' 라는 의구심이 생겨서 흥미도 없어지고, 작성 속도도 떨어지는 것 같다.
이래서 TDD 기반으로 테스트 코드를 작성하는 건가.... 싶은 생각이 든다.
테스트 코드를 어떻게 작성하는지 좀 더 살펴봐야 할 것 같다.
'강의 > TDD, Clean Code with Java 12기' 카테고리의 다른 글
Step5 - 자동차 경주(우승자) (14일차) (0) | 2021.08.03 |
---|---|
Step4 - 자동차 경주(우승자) - 피드백 반영 (11일차) (0) | 2021.07.29 |
4단계 자동차경주 (우승자) (8일차) - 피드백 반영 (0) | 2021.07.27 |
4단계 자동차경주 (우승자) (8일차) (0) | 2021.07.27 |
3단계 자동차경주 - 피드백반영 (6일차, 7일차) (0) | 2021.07.25 |