Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
62e0cd4
docs: update implementation function docs
Untaini Mar 14, 2023
5e38f7e
feat: getCarListFromInput function
Untaini Mar 14, 2023
ecddec2
refactor: change getCarListFromInput function return type
Untaini Mar 15, 2023
c4b9717
refactor: Add InputUtils class
Untaini Mar 15, 2023
1b780d9
feat: getTotalRoundFromInput
Untaini Mar 15, 2023
32e13eb
feat: move and print function in Car class
Untaini Mar 15, 2023
5e45720
feat: get function in Car class
Untaini Mar 15, 2023
cd48ee3
feat: playAllRounds function
Untaini Mar 15, 2023
04615d7
refactor: change incorrect function name
Untaini Mar 15, 2023
06b3c8b
feat: printWinners function
Untaini Mar 15, 2023
65b263b
refactor: change error tag
Untaini Mar 15, 2023
f788aef
refactor: change pickNumberInRange import
Untaini Mar 15, 2023
681039c
style: change newline format and function order
Untaini Mar 15, 2023
be24ffb
feat: add StringValidator class
Untaini Mar 18, 2023
b118782
refactor: add PrintManager class
Untaini Mar 18, 2023
56122be
feat: add StringConvertor class
Untaini Mar 18, 2023
e7f1526
feat: add Round class
Untaini Mar 18, 2023
0324770
feat: add Game class
Untaini Mar 18, 2023
1dabec0
feat: createCar func
Untaini Mar 18, 2023
4b6fc87
refactor: remove unused function
Untaini Mar 18, 2023
8dc465d
refactor: use Game instance and remove unused function
Untaini Mar 18, 2023
1fee1e4
feat: getWinnersString function
Untaini Mar 18, 2023
1d07b2b
refactor: add tool, game package
Untaini Mar 18, 2023
66ed54a
refactor: change canCarName to cannotUseCarName
Untaini Mar 18, 2023
e76735a
refactor: move func from PrintManger to ListConvertor
Untaini Mar 25, 2023
dc2657f
refactor: change var name and func name for deleting type in name
Untaini Mar 25, 2023
63b1b06
refactor: declare that the constructor cannot be called
Untaini Mar 25, 2023
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
9 changes: 9 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h1>구현할 기능 목록</h1>

- ### 사용자로부터 자동차 이름 입력받고 유효성 검사하기
- ### 사용자로부터 시도할 횟수 입력받고 유효성 검사하기
- ### 시도할 횟수만큼 라운드 반복하기
- ### 모든 자동차 전진 또는 정지하기
- ### 자동차의 상태 출력하기
- ### 최종 우승자 찾기
- ### 최종 우승자 출력하기
12 changes: 11 additions & 1 deletion src/main/java/racingcar/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
package racingcar;

import racingcar.game.Car;
import racingcar.game.Game;
import racingcar.tool.InputUtils;

import java.util.List;

public class Application {
public static void main(String[] args) {
// TODO 구현 진행
List<Car> carList = InputUtils.getCarListFromInput();
int totalRound = InputUtils.getTotalRoundFromInput();
Game game = new Game(carList, totalRound);

game.start();
}
}
12 changes: 0 additions & 12 deletions src/main/java/racingcar/Car.java

This file was deleted.

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

import camp.nextstep.edu.missionutils.Randoms;

import racingcar.tool.StringValidator;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


public class Car {
private final String name;
private int position = 0;

public Car(String name) {
this.name = name;
}

public static Car createCar(String carName) {
StringValidator.cannotUseCarName(carName);

return new Car(carName);
}

public int getPosition() {
return this.position;
}

public String getName() {
return this.name;
}

public String toString() {
return String.format("%s : %s",this.name, getMoveString());
}

public void move() {
if (Randoms.pickNumberInRange(0, 9) >= 4) {
this.position += 1;
}
}

private String getMoveString() {

Choose a reason for hiding this comment

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

함수형 프로그래밍을 통해서 깔끔하게 표현하신 것이 인상 깊었습니다.
그리고 이 함수를 PrintManager가 아닌 Car에 구현하신 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

이동한 거리도 Car의 정보라 생각했기 때문입니다. 그래서 Car의 정보를 문자열로 표현하는 방법을 toString에서 정의하고 있으니, 거리를 문자열로 표현한 것도 Car에서 구현하면 되겠다고 생각했습니다.

List<String> movePositionList = Arrays.stream(new String[this.position])
.map(s -> "-")
.collect(Collectors.toList());

return String.join("", movePositionList);
}
}
45 changes: 45 additions & 0 deletions src/main/java/racingcar/game/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package racingcar.game;

import racingcar.tool.PrintManager;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class Game {

private final List<Car> carList;

Choose a reason for hiding this comment

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

변수명에 자료형이 들어가지 않는 것이 더 좋다고 알고 있습니다! 복수형으로 표현하는 것은 어떨까요?
참고 : https://tecoble.techcourse.co.kr/post/2020-04-24-variable_naming/

Copy link
Author

Choose a reason for hiding this comment

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

덕분에 변수 네이밍에 대해서 검색을 좀 더 할 수 있었습니다!
List 타입에 대해서는 List라는 이름을 사용하는 것을 허용한다는 글도 있었습니다.
하지만 차후 List 타입이 아닌 Set이나 다른 타입을 사용할 가능성이 있는 변수에 대해서는 복수형을 사용하는 것이 더 낫다고 하네요 :)
그래서 이 부분은 복수형으로 표현하는 것이 유지보수를 위해서는 더 나은 선택으로 보입니다. 감사합니다!

private final int totalRound;

public Game(List<Car> carList, int totalRound) {
this.carList = carList;
this.totalRound = totalRound;
}

public void start() {
Round round = new Round(carList);

PrintManager.printGameResultHead();
while (round.getCurrentRound() <= totalRound) {
round.play();
}

List<Car> winnerList = findWinners();
PrintManager.printWinnersName(winnerList);
}

private List<Car> findWinners() {
if (carList.size() == 0) {

Choose a reason for hiding this comment

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

예외 처리에서 CarList의 size()가 0인 경우는 이미 배제하셨던데 혹시 findWinners() 함수에서 0인 경우를 따로 처리하신 것이 어떤 의미가 있는건지 알고 싶습니다..!!

Copy link
Author

Choose a reason for hiding this comment

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

그 이유는 Game 객체를 생성할 때 받은 carList가 빈 리스트인지는 확인하지 않았기 때문입니다.

적어도 원소 하나는 있어야 그 아래 코드에서 오류가 발생하지 않기 때문에
빈 리스트인 경우에는 if문을 통해 빈 리스트를 반환해 주었습니다.
사실 carList를 반환해주어도 되지만, 비어있다는 것을 명확하게 표현하기 위해 Collections.emptyList()를 사용했습니다.

return Collections.emptyList();
}

int maxPosition = carList.stream()
.map(Car::getPosition)
.max(Integer::compareTo)
.get();

return carList.stream()
.filter(car -> car.getPosition() == maxPosition)
.collect(Collectors.toList());

Choose a reason for hiding this comment

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

stream을 잘 활용한 좋은 예인 것 같습니다. :)

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

import racingcar.tool.PrintManager;

import java.util.List;

public class Round {
private int currentRound;
private final List<Car> carList;

public Round(List<Car> carList) {
this.currentRound = 1;
this.carList = carList;
}

public int getCurrentRound() {
return this.currentRound;
}

public void play() {
moveAllCars();
PrintManager.printAllCarsStatus(carList);
this.currentRound += 1;
}

private void moveAllCars() {
carList.forEach(Car::move);
}

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

import camp.nextstep.edu.missionutils.Console;

import racingcar.game.Car;

import java.util.List;

public class InputUtils {

public static List<Car> getCarListFromInput() {
while (true) {
PrintManager.printCarNameInputDescription();

//for to split last comma, ' ' is going to be removed by trim func.
String carNames = Console.readLine() + ' ';
try {
return StringConvertor.convertIntoCarList(carNames);
} catch (IllegalArgumentException exception) {
PrintManager.printErrorMessage(exception.getMessage());
}
}
}

public static int getTotalRoundFromInput() {
while (true) {
PrintManager.printTotalRoundInputDescription();

String roundInput = Console.readLine();
try {
return StringConvertor.convertIntoNaturalNumber(roundInput);
} catch (IllegalArgumentException exception) {
PrintManager.printErrorMessage(exception.getMessage());
}
}
}

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

import racingcar.game.Car;

import java.util.List;
import java.util.stream.Collectors;

public class PrintManager {

public static void printCarNameInputDescription() {
System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
}

public static void printTotalRoundInputDescription() {

Choose a reason for hiding this comment

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

printCarNameInputDescription()이나 이 함수 같이 안내 문구만 각각 한 줄씩 따로 함수로 생성한 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

PrintManager는 출력과 관련된 함수들을 정의해둔 클래스입니다. 그래서 출력되는 문자열을 바꾸고 싶을 때는 PrintManager만 수정하면 되게끔 만들었습니다.

System.out.println("시도할 회수는 몇회인가요?");
}
Comment on lines +11 to +17

Choose a reason for hiding this comment

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

SRP를 위해 클래스의 책임이 명확히 구분된 것 같아서 좋습니다! 저도 해봐야겠네요


public static void printAllCarsStatus(List<Car> carList) {
carList.forEach(System.out::println);
System.out.println();
}

public static void printWinnersName(List<Car> carList) {
String winnersName = getWinnersString(carList);

System.out.printf("최종 우승자 : %s", winnersName);
}

private static String getWinnersString(List<Car> carList) {
List<String> winnerNameList = carList.stream()
.map(Car::getName)
.collect(Collectors.toList());

return String.join(", ", winnerNameList);
}

public static void printGameResultHead() {
System.out.println("\n실행 결과");
}

public static void printErrorMessage(String message) {
System.out.printf("[ERROR] %s\n", message);
}
}
31 changes: 31 additions & 0 deletions src/main/java/racingcar/tool/StringConvertor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package racingcar.tool;

import racingcar.game.Car;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StringConvertor {

public static List<Car> convertIntoCarList(String carNames) throws IllegalArgumentException {
return Arrays.stream(carNames.split(","))
.map(String::trim)
.map(Car::createCar)
.collect(Collectors.toList());

Choose a reason for hiding this comment

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

함수형 프로그래밍으로 구현하신 것이 정말 인상적인 것 같습니다.
혹시 이 코드에 대한 설명을 조금이나마 부탁드려도 괜찮을까요?

Copy link
Author

Choose a reason for hiding this comment

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

위의 코드를 말로 풀면 다음과 같습니다.

  1. carNames 문자열을 ','으로 나눕니다.
  2. 나눠진 문자열의 양쪽 공백을 삭제합니다.
  3. 각 문자열을 이름으로 가지는 Car 객체를 생성합니다.
  4. 리스트로 만들어 반환합니다.

코드를 해석하는데 조금 도움이 됐으면 좋겠네요 :)

Choose a reason for hiding this comment

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

감사합니다 :)

}

public static Integer convertIntoNaturalNumber(String targetString) throws IllegalArgumentException {
try {
int number = Integer.parseInt(targetString);

if (number < 1) {
throw new NumberFormatException();
}

return number;
} catch (NumberFormatException exception) {
throw new IllegalArgumentException("자연수가 아닌 값은 입력할 수 없습니다.");
}
}
}
13 changes: 13 additions & 0 deletions src/main/java/racingcar/tool/StringValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package racingcar.tool;

public class StringValidator {


public static void cannotUseCarName(String carName) throws IllegalArgumentException {
if (carName.length() == 0) {
throw new IllegalArgumentException("자동차 이름은 공백일 수 없습니다.");
} else if (carName.length() > 5) {
throw new IllegalArgumentException("자동차 이름은 최대 5자리입니다. 불가능한 이름: " + carName);
}
}
}