-
Notifications
You must be signed in to change notification settings - Fork 452
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[라빈] 자동차 경주 - TDD 미션 리뷰 요청입니다. (#68)
* 덧셈 기능 구현 * 입력된 문자열을 ,로 분리하는 기능 구현 * 커스텀 구분자에서 계산식만 추출한다. * README 수정 구분자를 추출하는 기능 추가 커스텀 구분자를 이용하여 숫자를 추출하는 기능 추가 커스텀 구분자를 추가하는 기능 추가 * 숫자 이외의 값이 입력될 경우 RuntimeException을 throw 한다 * refactor : depth 와 indent 수정 * 올바르지 않은 커스텀 구분자 문자열 입력 시 예외 출력 기능 구현 * 자동차 게임에 대한 README 수정 문자열 덧셈 계산기 구현 * refact : 커스텀 구분자를 입력했을 때 \n -> \\n 으로 입력되는 것을 테스트하고 모든 테스트 케이스를 수정 / Calculate.java 에서 모든 메소드를 \n -> \\n 으로 수정 * doc : README 수정 * feat : 자동차 이름을 입력받고 검증하는 테스트 코드 작성 / 자동차 이름을 검증하고 저장하는 클래스 구현 * feat :시도 횟수를 검증하고 저장하는 테스트 코드 작성 / 시도 횟수를 검증하고 저장하는 클래스 구현 * 작업을 위해 gitignore 수정 * feat : gitignore 재수정 / Car 객체 구현 / 자동차의 랜덤 숫자에 따라서 진행을 판단하는 테스트 코드 작성 * feat : 랜덤 숫자 생성 기능 구현 / 랜덤 숫자를 생성하는 메소드 테스트 코드 작성 * feat : 최종 우승자를 계산하는 메서드 구현 및 테스트 코드 작성 * feat : 자동차 경주 게임 전체 기능 구현 및 테스트 코드 작성 * refactor : 조건 검사를 메소드로 추출 * refactor : 조건 검사를 메소드로 추출 * refactor : DisplayName 추가 / 메소드 이름 수정 / ParameterizedTest 를 이용해서 테스트 케이스 추가 * refactor : 계산기와 자동차 경주 게임의 메인 함수 패키지 별로 분리 / 실행 결과 출력 추가 / 자동차 경주 게임의 우승자 이름을 만드는 기능을 메소드로 추출 * refactor : 클래스 이름 수정 / null 또는 빈 문자열 입력시 0을 반환하는 덧셈 기능 구현 및 테스트 코드 작성 / 한 자리의 숫자를 입력시 그 숫자를 그대로 반환하는 덧셈 기능 구현 및 테스트 코드 작성 * refactor : 쉼표구분자로 문자열을 나눠서 숫자들의 합을 출력하는 테스트 코드 작성 * refactor : 쉼표 또는 콜론 구분자를 이용해 문자열을 나눠서 숫자들의 합을 출력 테스트 추가 / 코드 리팩토링 * refactor : Matcher 와 Pattern 클래스를 사용해 정규표현식을 사용한 문자열 나누는 API 테스트 코드 작성 / 커스텀 구분자로 문자열을 나눠서 숫자들의 합을 반환 테스트 코드 추가(프로덕션 코드 작성 해야함) * refactor : 커스텀 구분자가 포함된 문자열 또는 기본 문자열이 입력되었을 때 계산 로직 수정 * refactor : 음수를 전달할 경우 RuntimeException 예외가 발생하는 기능 구현 및 테스트 코드 작성 * refactor : 클래스 변수 제거 * refactor : 문자열 계산기 기능 구현 * refactor : 사용되는 문자열을 클래스로 분리 / 결과를 출력하는 기능을 StringBuilder 를 사용해서 구현 * refactor : 사용되는 문자열을 클래스로 분리 / 문자를 더하는 기능을 모두 StringBuilder 를 사용해서 구현 * refactor : MVC 패턴을 적용하기 위해 디렉토리 구조 변경 * refactor : Car 객체만 테스트 하는 클래스로 이름 변경 * refactor : Car 에서 Position 상태를 클래스로 추출 * refactor : CarTest -> CarNameTest 로 테스트 클래스 별로 하나의 클래스만 테스트 하도록 변경 / 컴파일 안되던 문제 수정 * refactor : CarName -> CarNames 로 일급 컬렉션으로 바꿈 * refactor : CarNames 에서 메소드 이름, 파라미터 이름 이해하기 쉽게 수정 / CarNamesTest 에서 테스트 목적에 맞게 DisplayName 수정 * refactor : Position 클래스를 테스트 하는 PositionTest 클래스 추가 / Position 클래스의 생성자 변경 * refactor : Car 의 Position 을 변경하는 메소드 이름 수정 / Car 의 carName 을 클래스로 추출 / CarName 을 테스트 하는 코드 추가 * refactor : Car 의 carName 을 CarName 클래스로 변경 / CarName 과 Position 클래스에 equals 메소드 오버라이드 * 임시 푸쉬 * refact : Car 클래스 equals 메소드, hashCode 메소드 오버라이드 수정 / Cars 일급 컬렉션으로 포장 * refact : RacingLab 클래스 메소드 이름 수정 / RacingLab 클래스 테스트 클래스 추가 * refact : RandomNumber 를 클래스로 추출 / RandomNumber 가 범위에 맞게 생성되는지 테스트 / RacingGame 에서 RacingLab 만큼 게임이 실행되었는지 확인하는 메소드 테스트 추가 * refact : controller 구현 / viewer 를 input 과 output viewer 로 나눠서 구현 / 테스트 코드 정리 * refact : Car 의 moveCar() -> move() 로 이름 변경 / Car 를 생성할 때 CarName 을 직접 받아오도록 변경 / 직관성이 높아지도록 각 클래스마다 상수 변경 / Cars 의 addCar() -> initializeCar() 로 변경 / PositionTest 에서 move 를 할 때 예외가 발생하지 않도록 설계했으므로 테스트 삭제 / CarTest 에 자동차의 위치가 잘 변하는지 테스트 케이스 추가
- Loading branch information
Showing
23 changed files
with
824 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,69 @@ | ||
# java-racingcar | ||
자동차 경주 게임 미션 저장소 | ||
|
||
## 우아한테크코스 코드리뷰 | ||
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
## 문자열 덧셈 계산기 | ||
요구사항 | ||
- 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환 (예: “” => 0, "1,2" => 3, "1,2,3" => 6, “1,2:3” => 6) | ||
- 앞의 기본 구분자(쉼표, 콜론)외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 “//”와 “\n” 사이에 위치하는 문자를 커스텀 구분자로 사용한다. 예를 들어 “//;\n1;2;3”과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다. | ||
- 문자열 계산기에 숫자 이외의 값 또는 음수를 전달하는 경우 RuntimeException 예외를 throw한다. | ||
- 문자열을 입력 받는다 | ||
- 예외사항 | ||
- 숫자 또는 슬래쉬로 시작하지 않는 문자열은 RuntimeException 예외를 throw 한다. | ||
- 숫자로 시작하는 문자열의 구분자는 , 또는 : 만 허용된다. | ||
- 커스텀 구분자를 // 와 \n 로 감싸지 않으면 RuntimException 예외를 throw 한다. | ||
|
||
프로그래밍 요구사항 | ||
- indent(들여쓰기) depth를 2단계에서 1단계로 줄여라. | ||
- depth의 경우 if문을 사용하는 경우 1단계의 depth가 증가한다. if문 안에 while문을 사용한다면 depth가 2단계가 된다. | ||
- 메소드의 크기가 최대 10라인을 넘지 않도록 구현한다. | ||
- method가 한 가지 일만 하도록 최대한 작게 만들어라. | ||
- else를 사용하지 마라. | ||
|
||
## 자동차 경주 | ||
### 기능 요구사항 | ||
- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. | ||
- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. | ||
- 횟수는 음수이면 안된다. | ||
- 횟수는 양의 정수이어야 한다. | ||
- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. | ||
- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. | ||
- 이름에는 쉼표를 제외한 모든 문자가 포함 가능하다. | ||
- 길이가 5 이하인 문자열이면 자동차 이름으로 할 수 있다. | ||
- 공백 또는 쉼표로 시작하는 문자열을 입력하면 안된다.(ex: ,lavine) | ||
- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다. | ||
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. | ||
|
||
### 프로그래밍 요구사항 | ||
- 모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외 | ||
- 자바 코드 컨벤션을 지키면서 프로그래밍한다. | ||
- 참고문서: https://google.github.io/styleguide/javaguide.html 또는 https://myeonguni.tistory.com/1596 | ||
- indent(인덴트, 들여쓰기) depth를 3을 넘지 않도록 구현한다. 2까지만 허용한다. | ||
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. | ||
- 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다. | ||
- else 예약어를 쓰지 않는다. | ||
- 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다. | ||
- else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. | ||
- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. | ||
- 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다. | ||
|
||
### 기능 목록 및 commit 로그 요구사항 | ||
- 기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리해 추가한다. | ||
- git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다. | ||
|
||
### 기능 목록 | ||
- CarName | ||
- 자동차 이름을 검증하고 저장하는 클래스 | ||
- RacingLab | ||
- 시도 횟수를 검증하고 저장하는 클래스 | ||
- Output | ||
- 실행 결과를 출력하는 메소드 | ||
- 우승자를 출력하는 메소드 | ||
- Car | ||
- 이름과 위치를 필드로 갖는다. | ||
- 랜덤 숫자에 따라서 진행을 판단하는 메소드 | ||
- Racing | ||
- 입력받은 시도 횟수만큼 반복하는 메소드 | ||
- 랜덤 숫자를 생성하는 메소드 | ||
- 우승자를 판단하는 메소드 | ||
- Application | ||
- 자동차 경주 실행 |
35 changes: 35 additions & 0 deletions
35
src/main/java/application/calculator/CalculatorApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package application.calculator; | ||
|
||
import java.util.Scanner; | ||
|
||
public class CalculatorApplication { | ||
private static Scanner scanner = new Scanner(System.in); | ||
|
||
private static void runCalculator() { | ||
try { | ||
String userInputText = getUserInputText(); | ||
int calculateResult = StringCalculator.splitAndSum(userInputText); | ||
String result = makeResultString(userInputText, calculateResult); | ||
System.out.println(result); | ||
} catch (Exception e) { | ||
System.out.println(e.getMessage()); | ||
runCalculator(); | ||
} | ||
} | ||
|
||
private static String makeResultString(String userInputText, int calculateResult) { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append(userInputText); | ||
sb.append(ConstantForCalculator.ARROW); | ||
sb.append(calculateResult); | ||
return sb.toString(); | ||
} | ||
|
||
private static String getUserInputText() { | ||
return scanner.nextLine(); | ||
} | ||
|
||
public static void main(String[] args) { | ||
runCalculator(); | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/application/calculator/ConstantForCalculator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package application.calculator; | ||
|
||
public class ConstantForCalculator { | ||
public final static String ARROW = " => "; | ||
public final static String BIT_OR_OPERATOR = "|"; | ||
public final static String DEFAULT_DELIMITER = ",|:"; | ||
public final static String REGULAR_EXPRESSION = "//(.)\\\\n(.*)"; | ||
} |
72 changes: 72 additions & 0 deletions
72
src/main/java/application/calculator/StringCalculator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package application.calculator; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class StringCalculator { | ||
public static int splitAndSum(String text) { | ||
if (isNullOrEmptyText(text)) { | ||
return 0; | ||
} | ||
String delimiter = ConstantForCalculator.DEFAULT_DELIMITER; | ||
String[] splitedText = splitText(text, delimiter); | ||
List<Integer> numbers = new ArrayList<>(); | ||
splitText(text, delimiter); | ||
addNumbers(splitedText, numbers); | ||
return calculateNumberSum(numbers); | ||
} | ||
|
||
private static String[] splitText(String text, String delimiter) { | ||
Matcher matcher = Pattern.compile(ConstantForCalculator.REGULAR_EXPRESSION).matcher(text); | ||
if (matcher.find()) { | ||
String customDelimiter = matcher.group(1); | ||
delimiter = addCustomDelimiter(delimiter, customDelimiter); | ||
return matcher.group(2).split(delimiter); | ||
} | ||
return text.split(delimiter); | ||
} | ||
|
||
private static String addCustomDelimiter(String delimiter, String customDelimiter) { | ||
StringBuilder sb = new StringBuilder(); | ||
sb.append(delimiter); | ||
sb.append(ConstantForCalculator.BIT_OR_OPERATOR); | ||
sb.append(customDelimiter); | ||
return sb.toString(); | ||
} | ||
|
||
private static int calculateNumberSum(List<Integer> numbers) { | ||
int sum = 0; | ||
for (int integer : numbers) { | ||
sum = sum + integer; | ||
} | ||
return sum; | ||
} | ||
|
||
private static void addNumbers(String[] splitString, List<Integer> numbers) { | ||
for (String string : splitString) { | ||
int convertNumber = getConvertNumber(string); | ||
throwRuntimeExceptionWhenNegativeNumber(convertNumber); | ||
numbers.add(convertNumber); | ||
} | ||
} | ||
|
||
private static void throwRuntimeExceptionWhenNegativeNumber(int number) { | ||
if (number < 0) { | ||
throw new RuntimeException("음수를 입력하였습니다."); | ||
} | ||
} | ||
|
||
private static int getConvertNumber(String string) { | ||
try { | ||
return Integer.parseInt(string); | ||
} catch (Exception e) { | ||
throw new RuntimeException("잘못된 수를 입력하였습니다."); | ||
} | ||
} | ||
|
||
private static boolean isNullOrEmptyText(String text) { | ||
return text == null || text.isEmpty(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package application.racing; | ||
|
||
import application.racing.controller.RacingGameController; | ||
|
||
public class RacingApplication { | ||
public static void main(String[] args) { | ||
RacingGameController.run(); | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
src/main/java/application/racing/controller/RacingGameController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package application.racing.controller; | ||
|
||
import application.racing.domain.Cars; | ||
import application.racing.domain.RacingGame; | ||
import application.racing.domain.RacingLab; | ||
import application.racing.view.InputViewer; | ||
import application.racing.view.OutputViewer; | ||
|
||
public class RacingGameController { | ||
private static Cars cars; | ||
private static RacingLab racingLab; | ||
private static RacingGame racingGame; | ||
|
||
public static void run() { | ||
initializeCars(); | ||
initializeRacingLab(); | ||
race(); | ||
OutputViewer.printWinner(cars.findWinner()); | ||
} | ||
|
||
private static void initializeCars() { | ||
boolean flag = false; | ||
while (!flag) { | ||
try { | ||
cars = new Cars(InputViewer.getCarsName()); | ||
flag = true; | ||
} catch (IllegalArgumentException e) { | ||
OutputViewer.printErrorMessage(e.getMessage()); | ||
} | ||
} | ||
} | ||
|
||
private static void initializeRacingLab() { | ||
boolean flag = false; | ||
while (!flag) { | ||
try { | ||
racingLab = new RacingLab(InputViewer.getRacingLab()); | ||
flag = true; | ||
} catch (IllegalArgumentException e) { | ||
OutputViewer.printErrorMessage(e.getMessage()); | ||
} | ||
} | ||
} | ||
|
||
private static void race() { | ||
racingGame = new RacingGame(); | ||
OutputViewer.printRacingResultMessage(); | ||
while (!racingGame.isEnd(racingLab)) { | ||
racingGame.raceOneLab(cars); | ||
OutputViewer.printPositionDuringRace(cars); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package application.racing.domain; | ||
|
||
public class Car { | ||
private CarName carName; | ||
private Position position; | ||
|
||
public Car(CarName carName) { | ||
this.carName = carName; | ||
this.position = new Position(); | ||
} | ||
|
||
public void move(int number) { | ||
this.position.move(number); | ||
} | ||
|
||
public boolean isMaxPosition(int maxPosition) { | ||
return this.getPosition() == maxPosition; | ||
} | ||
|
||
public int getPosition() { | ||
return this.position.getPosition(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return this.carName.toString(); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (o == this) { | ||
return true; | ||
} | ||
if (o == null || !(o instanceof Car)) { | ||
return false; | ||
} | ||
Car compareCar = (Car) o; | ||
return compareCar.carName.equals(this.carName) | ||
&& compareCar.position.equals(this.position); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return 31 * (this.carName.hashCode() + this.position.hashCode()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package application.racing.domain; | ||
|
||
public class CarName { | ||
private final static String BLANK = " "; | ||
private final static int MAX_NAME_LENGTH = 5; | ||
|
||
private String carName; | ||
|
||
public CarName(String carName) { | ||
validateCarNameFormat(carName); | ||
validateCarNameContainBlank(carName); | ||
validateCarNameLength(carName); | ||
this.carName = carName; | ||
} | ||
|
||
private void validateCarNameFormat(String carName) { | ||
if (carName == null || carName.isEmpty()) { | ||
throw new IllegalArgumentException("이름을 잘못 입력하였습니다."); | ||
} | ||
} | ||
|
||
private void validateCarNameContainBlank(String carName) { | ||
if (carName.contains(BLANK)) { | ||
throw new IllegalArgumentException("공백을 포함한 이름을 입력하였습니다."); | ||
} | ||
} | ||
|
||
private void validateCarNameLength(String carName) { | ||
if (carName.length() > MAX_NAME_LENGTH) { | ||
throw new IllegalArgumentException("5글자 초과의 자동차 이름을 입력하였습니다."); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (o == this) { | ||
return true; | ||
} | ||
if (o == null || !(o instanceof CarName)) { | ||
return false; | ||
} | ||
CarName compareCarName = (CarName) o; | ||
return compareCarName.carName.equals(this.carName); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return this.carName.hashCode() * 31; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return this.carName; | ||
} | ||
} |
Oops, something went wrong.