Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
49f85d1
docs: 문자열 덧셈 계산기 기능 구현 목록 정리
Gyuchool Feb 10, 2022
5e3881e
test: null 또는 빈 문자일떄 0을 반환하는지 확인하는 테스트 작성
Gyuchool Feb 10, 2022
1f3c00c
feat: null 또는 빈문자일때 0을 반환하는 기능 구현
Gyuchool Feb 10, 2022
59d00b5
docs: 기능 추가에 따른 readme 업데이트
Gyuchool Feb 10, 2022
eca16e6
test: 숫자하나일때 그대로 반환하는 테스트
Gyuchool Feb 10, 2022
a04c03e
docs: 기능 구현에 따른 readme 업데이트
Gyuchool Feb 10, 2022
4a90646
docs: 기능 구현 목록 수정
Gyuchool Feb 10, 2022
eac49e6
test: 쉼표 또는 콜론 구분자를 통한 덧셈 테스트
Gyuchool Feb 10, 2022
f0f9b04
feat: 쉼표 또는 콜론 구분자를 통한 덧셈 기능 구현
Gyuchool Feb 10, 2022
39fa96d
test: 커스텀 구분자를 통한 덧셈 테스트
Gyuchool Feb 10, 2022
ded1a80
feat: 커스텀 구분자를 통한 덧셈 기능 구현
Gyuchool Feb 10, 2022
b6b220f
test: 자연수가 아니면 예외처리 테스트
Gyuchool Feb 10, 2022
8c161f9
feat: 자연수가 아니면 예외처리 기능 구현
Gyuchool Feb 10, 2022
61bca7a
feat: main 클래스 생성 및 UI 클래스 추가
Gyuchool Feb 10, 2022
3c3b558
refactor: split 매서드 분리
Gyuchool Feb 10, 2022
0b98acc
docs(racingcar): 자동차 경주 기능 구현 목록 정리
Gyuchool Feb 10, 2022
d3765bc
feat: 0~9까지 랜덤 값중 4이상이면 전진하는 기능 구현
Gyuchool Feb 11, 2022
4a5888c
feat: 이름을 반환하는 기능 구현 및 현재 위치를 반환하는 기능 구현
Gyuchool Feb 11, 2022
7665e60
feat: 우승자를 결정하는 기능 구현
Gyuchool Feb 11, 2022
be0fded
feat: 우승자가 여러명일 경우 ','를 이용하여 구분하는 기능 구현
Gyuchool Feb 11, 2022
6471eea
feat: 자동차가 1개 이하 일때 예외 처리
Gyuchool Feb 11, 2022
3e48210
feat: 자동차 이름 입력에 빈칸이 들어왔을때 예외 처리
Gyuchool Feb 11, 2022
1f1e6a5
feat: 6자 이상인 자동차 이름 예외 처리
Gyuchool Feb 11, 2022
303258a
feat: 중복되는 이름 예외 처리
Gyuchool Feb 11, 2022
e9a6f32
feat: 유저 인풋이 ','로 끝나면 예외 처리
Gyuchool Feb 11, 2022
24109dd
feat: 자동차의 이름 입력받는 기능 구현 및 시도할 횟수를 입력받는 기능 구현
Gyuchool Feb 11, 2022
295d7f8
feat: 시도횟수가 자연수가 아니면 예외 처리
Gyuchool Feb 11, 2022
e8d1430
feat: 매 횟수마다 자동차들의 이름과 위치를 출력하는 기능 구현
Gyuchool Feb 11, 2022
5196ae5
feat: 메인함수 추가 및 구현
Gyuchool Feb 11, 2022
06c58c1
feat: 우승자를 출력해주는 기능 구현
Gyuchool Feb 11, 2022
45df2a7
refactor: 패키지 정리
Gyuchool Feb 11, 2022
fd509e8
refactor: 유효성검사 적용
Gyuchool Feb 11, 2022
052f298
refactor: 매서드 이름 가독성 좋도록 변경
Gyuchool Feb 11, 2022
04c79fb
refactor: validation test 코드 분리
Gyuchool Feb 11, 2022
40f917e
refactor: 구분자 상수화
Gyuchool Feb 11, 2022
f7612cf
refactor: winner를 Game클래스 안에서 결정하도록 책임이동
Gyuchool Feb 11, 2022
1ee5a4b
fix: 옳지않은 입력 값이 들어왔을때 바로 예외처리 되도록 수정
Gyuchool Feb 11, 2022
76ae8ff
refactor: 자바 컨벤션에 맞게 수정 및 매직넘버 상수화
Gyuchool Feb 12, 2022
2ccbd7f
refactor: 매서드 하나당 한 가지 일만 처리하도록 분리
Gyuchool Feb 12, 2022
d64ecbf
refactor: 범용적인 이름으로 인자 변경
Gyuchool Feb 14, 2022
62b3f75
refactor: 자동차에게 이름의 길이를 질의하도록 수정
Gyuchool Feb 14, 2022
086a61a
fix: 매서드 분리에 따른 테스트코드 수정
Gyuchool Feb 14, 2022
0462723
refactor: input값들을 view단에서 검증후 넘겨주도록 수정
Gyuchool Feb 15, 2022
114ade9
test: 입력한 이름이 객체로 생성되었는지 확인하는 테스트
Gyuchool Feb 15, 2022
bbd7f4d
test: 최대 거리 이동한 자동차를 뽑는 테스트
Gyuchool Feb 15, 2022
9f1d8da
refactor: 사용하지 않는 import 제거
Gyuchool Feb 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/calculator-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 문자열 덧셈 계산기😎

## 기능 구현 목록 👋

- [x] 문자열 또는 null 값을 입력할 경우 `0`을 반환해야 한다.(예 : “” => 0, null => 0)
- [x] 숫자 하나를 문자열로 입력할 경우 해당 숫자를 반환한다.(예 : “1”)
- [x] 쉼표 또는 콜론으로 나눈 숫자를 더한다.
- [x] `//`와 `\n` 문자 사이에 커스텀 구분자를 지정할 수 있고 해당 구분자로 숫자를 나누고 더한다. (“//;\n1;2;3” => 6)

## 예외 처리 사항

- [x] 음수나 문자가 추출된 경우 RuntimeException 예외가 발생해야 한다.
34 changes: 34 additions & 0 deletions docs/racingcar-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 자동차 경주 🏎

## 🎮 기능 구현 목록

### 입력 기능

- [x] 자동차의 이름을 입력 받는 기능 구현
- [x] 시도할 횟수를 입력 받는 기능 구현
- 예외처리
- [x] 정수가 아니면 예외 처리
- [x] 0 이하 입력은 예외 처리

### 레이싱

- [x] 0~9까지 랜덤 값중 4이상이면 전진하는 기능 구현
- [x] 이름을 반환하는 기능 구현
- [x] 현재 위치를 반환하는 기능 구현

- 예외 처리
- [x] 자동차가 1개 이하 일때, 예외 처리(경쟁자가 없으면 경주가 아니기 때문에)
- [x] 자동차 이름에 빈칸이 입력되었을때 예외 처리
- [x] 6자 이상인 자동차 이름 예외 처리
- [x] 중복되는 이름 예외 처리
- [x] 유저 인풋이 ',' 로 끝나면 예외 처리

### 우승자 결정 기능

- [x] 우승자를 결정하는 기능 구현
- [x] 우승자가 여러명일 경우 ','를 이용하여 구분하는 기능 구현

### 출력 기능

- [x] 매 횟수마다 자동차들의 이름과 위치를 출력하는 기능 구현
- [x] 우승자가 2명 이상일 경우에는 쉼표로 구분해서 출력하는 기능 구현
41 changes: 41 additions & 0 deletions src/main/java/calculator/StringCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package calculator;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringCalculator {

private static final String PATTERN = "\\d+";

public static int splitAndSum(String input) {
if (input == null || input.isEmpty()) {
return 0;
}
String[] separatedValues = split(input);
return getSum(separatedValues);
}

private static String[] split(String input) {
Matcher matcher = Pattern.compile("//(.)\n(.*)").matcher(input);
if (matcher.find()) {
String customDelimiter = matcher.group(1);
return matcher.group(2).split(customDelimiter);
}
return input.split(",|:");
}

private static int getSum(String[] separatedValues) {
int sum = 0;
for (String separatedValue : separatedValues) {
checkNaturalNumber(separatedValue);
sum += Integer.parseInt(separatedValue);
}
return sum;
}

private static void checkNaturalNumber(String target) {
if (!Pattern.matches(PATTERN, target)){
throw new RuntimeException();
}
}
}
10 changes: 10 additions & 0 deletions src/main/java/calculator/StringCalculatorMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package calculator;

public class StringCalculatorMain {
public static void main(String[] args) {
String input = StringCalculatorUi.getInput();
int result = StringCalculator.splitAndSum(input);
StringCalculatorUi.printOutput(result);
}

}
26 changes: 26 additions & 0 deletions src/main/java/calculator/StringCalculatorUi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package calculator;

import java.util.Scanner;

public class StringCalculatorUi {

private static final String INPUT_MESSAGE = "더할 값을 입력하세요:";
private static final String OUTPUT_MESSAGE = "결과 값은 %d 입니다.\n";
private static final String CUSTOM_SEPARATOR_FIRST_CONDITION = "//";
private static final String CUSTOM_SEPARATOR_SECOND_CONDITION = "\n";

public static void printOutput(int output) {
System.out.printf(OUTPUT_MESSAGE, output);
}

public static String getInput() {
Scanner scanner = new Scanner(System.in);
System.out.print(INPUT_MESSAGE);
String input = scanner.nextLine();
if (input.contains(CUSTOM_SEPARATOR_FIRST_CONDITION)) {
String additionalInput = scanner.nextLine();
return input + CUSTOM_SEPARATOR_SECOND_CONDITION + additionalInput;
}
return input;
}
}
21 changes: 21 additions & 0 deletions src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package racingcar;

import racingcar.domain.RacingCars;
import racingcar.view.InputView;
import racingcar.view.OutputView;

public class Application {

public static void main(String[] args) {
RacingCars racingCars = new RacingCars();

racingCars.join(InputView.askCarName());
RacingGame racingGame = new RacingGame(racingCars);

racingGame.playGame(racingCars);

String winnersName = racingGame.getWinnersName();
OutputView.printWinners(winnersName);
}

}
66 changes: 66 additions & 0 deletions src/main/java/racingcar/RacingGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package racingcar;

import racingcar.domain.RacingCar;
import racingcar.domain.RacingCars;
import racingcar.validator.Validator;
import racingcar.view.InputView;
import racingcar.view.OutputView;

import java.util.ArrayList;
import java.util.List;

public class RacingGame {

private static final String WINNER_NAME_DELIMITER = ", ";
private final RacingCars racingCars;

public RacingGame() {
this.racingCars = new RacingCars();
}

public RacingGame(RacingCars racingCars) {
this.racingCars = racingCars;
}

public void playGame(RacingCars racingCars) {

String tryCount = InputView.askTryCount();
Validator.checkTryCountIsNaturalNumber(tryCount);
int trialCount = Validator.convertToInt(tryCount);

OutputView.printGameStartMessage();
for (int i = 0; i < trialCount; i++) {
moveCar(racingCars);
OutputView.printCurrentRacingSituation(racingCars);
}
}

private void moveCar(RacingCars racingCars) {
for (RacingCar racingCar : racingCars.getRacingCars()) {
racingCar.goOrStay(RandomGenerator.generateRandomNumber());
}
}

public String getWinnersName() {
ArrayList<RacingCar> winners = getWinners(racingCars);
ArrayList<String> winnersName = new ArrayList<>();
for (RacingCar winner : winners) {
winnersName.add(winner.getName());
}
return String.join(WINNER_NAME_DELIMITER, winnersName);
}

private ArrayList<RacingCar> getWinners(RacingCars racingCars) {

RacingCar racingCarOfMaxPosition = racingCars.getRacingCarWithMaxPosition();
List<RacingCar> racingCarList = racingCars.getRacingCars();
ArrayList<RacingCar> winners = new ArrayList<>();

for (RacingCar racingCar : racingCarList) {
if (racingCar.isSamePosition(racingCarOfMaxPosition)) {
winners.add(racingCar);
}
}
return winners;
}
}
13 changes: 13 additions & 0 deletions src/main/java/racingcar/RandomGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package racingcar;

import java.util.Random;

public class RandomGenerator {

private static final int MAX_RANDOM_NUMBER = 10;

public static int generateRandomNumber() {
Random random = new Random();
return random.nextInt(MAX_RANDOM_NUMBER);
}
}
36 changes: 36 additions & 0 deletions src/main/java/racingcar/domain/RacingCar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package racingcar.domain;

public class RacingCar {

private static final int MOVE_STANDARD = 4;
private final String name;
private int position;

private RacingCar(String name) {
this.position = 0;
this.name = name;
}

public static RacingCar generateRacingCar(String name) {
return new RacingCar(name);
}

public int getPosition() {
return position;
}

public String getName() {
return name;
}

public void goOrStay(int number) {
if (number >= MOVE_STANDARD) {
position++;
}
}

public boolean isSamePosition(RacingCar racingCar) {
return this.position == racingCar.getPosition();
}

}
32 changes: 32 additions & 0 deletions src/main/java/racingcar/domain/RacingCars.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package racingcar.domain;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class RacingCars {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인을 잘 만들어주셨어요~
정의된 도메인에 대한 테스트는 꼭 필요한데요. 이 도메인은 테스트를 어떻게 하면 좋을까요? 🤔


private final List<RacingCar> racingCars;

public RacingCars() {
racingCars = new ArrayList<>();
}

public RacingCars(List<RacingCar> racingCars) {
this.racingCars = racingCars;
}

public void join(String[] validCarNames) {
for (String validCarName : validCarNames) {
racingCars.add(RacingCar.generateRacingCar(validCarName));
}
}

public List<RacingCar> getRacingCars() {
return racingCars;
}

public RacingCar getRacingCarWithMaxPosition() {
return racingCars.stream().max(Comparator.comparingInt(RacingCar::getPosition)).get();
}
}
86 changes: 86 additions & 0 deletions src/main/java/racingcar/validator/Validator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package racingcar.validator;

import java.util.*;

public class Validator {

public static final char MIN_NUMBER_CRITERIA = '0';
public static final char MAX_NUMBER_CRITERIA = '9';
private static final int MIN_COUNT_OF_CAR = 2;
private static final int CAR_NAME_STANDARD_SIZE = 5;
private static final String MIN_CAR_OF_COUNT_MESSAGE = "[ERROR] 자동차 개수는 2개 이상이어야 합니다.";
private static final String CAR_NAME_EMPTY_MESSAGE = "[ERROR] 자동차 이름은 빈칸일 수 없습니다.";
private static final String CAR_NAME_SIZE_MASSAGE = "[ERROR] 자동차 이름은 5자 이하여야 합니다.";
private static final String CAR_NAME_DUPLICATED_MESSAGE = "[ERROR] 자동차 이름이 중복되어선 안됩니다.";
private static final String LAST_INPUT_IS_COMMA = "[ERROR] 마지막 자동차 이름을 입력하지 않았습니다.";
private static final String TRY_COUNT_FORMAT_ERROR_MESSAGE = "[ERROR] 시도회수는 자연수여야 합니다.";

public static void checkCountOfCar(String[] racingCarNames) {
if (racingCarNames.length < MIN_COUNT_OF_CAR) {
throw new IllegalArgumentException(MIN_CAR_OF_COUNT_MESSAGE);
}
}

public static void checkCarsNameIsEmpty(String[] racingCarNames) {
for (String racingCarName : racingCarNames) {
validatorEmptyName(racingCarName);
}
}

private static void validatorEmptyName(String name) {
if ("".equals(name)) {
throw new IllegalArgumentException(CAR_NAME_EMPTY_MESSAGE);
}
}

public static void checkCarsNameSize(String[] racingCarNames) {
for (String racingCarName : racingCarNames) {
validatorCarNameSize(racingCarName);
}
}

private static void validatorCarNameSize(String racingCarName) {
if (racingCarName.length() > CAR_NAME_STANDARD_SIZE) {
throw new IllegalArgumentException(CAR_NAME_SIZE_MASSAGE);
}
}

public static void checkDuplicatedName(String[] racingCarNames) {
List<String> nameList = new ArrayList<>();
for (String racingCarName : racingCarNames) {
validatorDuplicatedName(nameList, racingCarName);
nameList.add(racingCarName);
}
}
Comment on lines +48 to +54

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정답은 없지만 고민해볼만한 포인트라 남겨요~
이름중복을 검사하는 책임을 누가가져가는게 좋을까요?
남기는 이유는 RacingCar라는 도메인을 만들어주셨기 때문이에요. Car들을 관리하는 책임이고 중복된 car들이 있는지 검사(관리)하는 역할을 부여하는게 나을지, 아니면 view에서 가져가는게 나을지 고민해보면 좋을 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Car들을 관리하는 RacingCars 도메인을 만들었기 때문에 여기서 관리하는건 어떤지 의문 던져주신 거 맞나요?
RacingCars가 관리 목적이라 중복된 Car들을 확인하는것도 제이 말씀대로 좋을 것 같습니다!
제가 위 코드처럼 Validator 클래스에서 검증 로직을 만들어 View단에서 검증했던 이유는 애초에 중복된 이름이 있으면 객체를 생성하지 않는 게 비용도 덜 들고 좋지 않을까 라고 생각했습니다!
이 부분은 좀 더 고민해보도록 할게요!! 좋은 의문 던져주셔서 감사합니다!


private static void validatorDuplicatedName(List<String> nameList, String name) {
if (nameList.contains(name)) {
throw new IllegalArgumentException(CAR_NAME_DUPLICATED_MESSAGE);
}
}

public static void checkHaveLastInputComma(String userInput) {
if (',' == userInput.charAt(userInput.length() - 1)) {
throw new IllegalArgumentException(LAST_INPUT_IS_COMMA);
}
}

public static void checkTryCountIsNaturalNumber(String tryCountInput) {
for (int i = 0; i < tryCountInput.length(); ++i) {
isNumber(tryCountInput.charAt(i));
}
}

private static void isNumber(char target) {
if (target < MIN_NUMBER_CRITERIA || target > MAX_NUMBER_CRITERIA) {
throw new IllegalArgumentException(TRY_COUNT_FORMAT_ERROR_MESSAGE);
}
}

public static int convertToInt(String tryCountInput) {
if (Integer.parseInt(tryCountInput) == 0) {
throw new IllegalArgumentException(TRY_COUNT_FORMAT_ERROR_MESSAGE);
}
return Integer.parseInt(tryCountInput);
}
}
Loading