Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step4 #4719

Open
wants to merge 15 commits into
base: prolkh
Choose a base branch
from
Open

Step4 #4719

Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 31 additions & 3 deletions src/main/java/racingcar/Car.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,35 @@ public class Car {
private static final int RANDOM_BOUND = 10;
private static final int PROCEED_THRESHOLD = 4;
private static final int INIT_POSITION = 1;
public static final int MAX_NAME_LENGTH = 5;

private final Random random = new Random();

private final String name;
private final List<Integer> positions;

Car(int trialCount) {
Car(String name, int trialCount) {
Copy link

Choose a reason for hiding this comment

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

이 생성자는 proceedOrStop 메소드가 항상 동일한 값을 리턴하지 않아서 테스트하기 어려울 것 같은데요.
어떻게 하면 테스트 할 수 있을까요?

this.name = name;
positions = new ArrayList<>();
int position = INIT_POSITION;
for(int i=0; i< trialCount; i++) {
for (int i = 0; i < trialCount; i++) {
addPosition(position);
position += proceedOrStop();
}
}

Car(List<Integer> positions) {
Car(String name, List<Integer> positions) {
validateCarName(name);
this.name = name;
this.positions = new ArrayList<>(positions);
}

private void validateCarName(String name) {
if (name.length() > MAX_NAME_LENGTH) {
throw new IllegalArgumentException("자동차 이름은 5자를 초과할 수 없다.");
}
}

public int getTrialCount() {
return positions.size();
}
Expand All @@ -34,6 +46,14 @@ public int getPosition(int index) {
return positions.get(index);
}

public int getLastPosition() {
if (positions.isEmpty()) {
return -1;
}

return positions.get(positions.size() - 1);
}

private void addPosition(int position) {
positions.add(position);
}
Expand All @@ -42,4 +62,12 @@ public int proceedOrStop() {
int randomValue = random.nextInt(RANDOM_BOUND);
return randomValue >= PROCEED_THRESHOLD ? 1 : 0;
}

public String getName() {
return name;
}

public List<Integer> getPositions() {
return positions;
}
}
32 changes: 32 additions & 0 deletions src/main/java/racingcar/CarCollection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package racingcar;

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

public class CarCollection {
Copy link

Choose a reason for hiding this comment

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

CarCollection 은 List 타입을 설명하기 위한 객체명인데요!
레이싱참가자들 과 같은 객체명으로 어떤 객체인지 표현하는게 좋지 않을까요?

private final List<Car> cars;

public CarCollection() {
this.cars = new ArrayList<>();
}

public CarCollection(List<Car> cars) {
this.cars = cars;
}

public void addCar(Car car) {
cars.add(car);
}

public void removeCar(Car car) {
Copy link

Choose a reason for hiding this comment

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

사용하지 않는 메소드는 삭제 해주세요 :)

cars.remove(car);
}

public int getCarCount() {
return cars.size();
}

public List<Car> getCars() {
return new ArrayList<>(cars);
}
}
26 changes: 16 additions & 10 deletions src/main/java/racingcar/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,32 @@

public class InputView {

public Racing init() {
Scanner scanner = new Scanner(System.in);
private InputViewDto inputViewDto;

Integer carCount = getCarCount(scanner);
Integer trialCount = getTrialCount(scanner);

System.out.println("carCount = " + carCount);
System.out.println("trialCount = " + trialCount);
InputView() {
Scanner scanner = new Scanner(System.in);

return new Racing(carCount, trialCount);
inputViewDto = new InputViewDto();
inputViewDto.setCarNames(getCarNames(scanner));
inputViewDto.setTrialCount(getTrialCount(scanner));
}

private Integer getCarCount(Scanner scanner) {
System.out.println("자동차 대수는 몇 대 인가요?");
return Integer.parseInt(scanner.nextLine());
private String getCarNames(Scanner scanner) {
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).");
return scanner.nextLine();
}

private static Integer getTrialCount(Scanner scanner) {
System.out.println("시도할 회수는 몇 회 인가요?");
return Integer.parseInt(scanner.nextLine());
}

public String getCarNames() {
return inputViewDto.getCarNames();
}

public int getTrialCount() {
return inputViewDto.getTrialCount();
}
}
24 changes: 24 additions & 0 deletions src/main/java/racingcar/InputViewDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package racingcar;

import java.util.Scanner;

public class InputViewDto {
private String carNames;
private int trialCount;

public String getCarNames() {
return carNames;
}

public void setCarNames(String carNames) {
this.carNames = carNames;
}

public int getTrialCount() {
return trialCount;
}

public void setTrialCount(int trialCount) {
this.trialCount = trialCount;
}
}
32 changes: 19 additions & 13 deletions src/main/java/racingcar/Racing.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
package racingcar;

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

public class Racing {
Copy link

Choose a reason for hiding this comment

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

Main 함수를 만들어서 프로그램이 실행 될 수 있게 만들어 주세요 :)

private final List<Car> cars;
// private final List<Car> cars;
private CarCollection carCollection;
Copy link

Choose a reason for hiding this comment

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

CarCollection 은 반드시 메인의 필드로 있어야 할까요?


public void main() {
InputView inputView = new InputView();
ResultView resultView = new ResultView();

Racing(int carCount, int trialCount) {
this.cars = createCars(carCount, trialCount);
buildCarCollection(inputView.getCarNames(), inputView.getTrialCount());
resultView.displayResults(carCollection);
resultView.displayWinnerMessage(carCollection);
}

private static List<Car> createCars(int carCount, int trialCount) {
List<Car> cars = new ArrayList<>();
for(int i=0; i<carCount; i++) {
cars.add(new Car(trialCount));
private void buildCarCollection(String carNames, int trialCount) {
Copy link

Choose a reason for hiding this comment

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

이 메소드는 CarCollection 내부로 이동 할 수 있지 않을까요?

carCollection = new CarCollection();

String[] names = carNames.split(",");

for (String name : names) {
Car car = new Car(name, trialCount);
carCollection.addCar(car);
}
return cars;
}

public List<Car> getCars() {
return new ArrayList<>(cars);
public CarCollection getCarCollection() {
return carCollection;
}
}
68 changes: 61 additions & 7 deletions src/main/java/racingcar/ResultView.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,79 @@
package racingcar;

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

public class ResultView {

public void displayResults(List<Car> cars) {
int trialCount = cars.get(0).getTrialCount();
public void displayResults(CarCollection carCollection) {
System.out.println(getResults(carCollection));
}

public String getResults(CarCollection carCollection) {
StringBuilder sb = new StringBuilder();
int trialCount = carCollection.getCars().get(0).getTrialCount();

for (int i = 0; i < trialCount; i++) {
displayColumn(cars, i);
System.out.println();
sb.append(getColumn(carCollection, i));
sb.append(System.lineSeparator());
}

return sb.toString();
}

private void displayColumn(List<Car> cars, int columnIndex) {
for (Car car : cars) {
System.out.println(getPositionLine(car, columnIndex));
private String getColumn(CarCollection carCollection, int index) {
StringBuilder sb = new StringBuilder();

for (Car car : carCollection.getCars()) {
sb.append(car.getName())
.append(" : ")
.append(getPositionLine(car, index))
.append(System.lineSeparator());
}

return sb.toString();
}


private String getPositionLine(Car car, int columnIndex) {
return "-".repeat(car.getPosition(columnIndex));
}

public void displayWinnerMessage(CarCollection carCollection) {
System.out.println(getWinnerMessage(carCollection));
}

public String getWinnerMessage(CarCollection carCollection) {
String winner = getWinner(carCollection, getBiggestLastPosition(carCollection));

return winner + "가 최종 우승했습니다.";
}

private int getBiggestLastPosition(CarCollection carCollection) {
Copy link

Choose a reason for hiding this comment

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

이 메소드는 우승자를 구할때 필요한 도메인 로직으로 보이는데요!
View 객체가 아닌 Domain 객체 내부로 이동해야 하지 않을까요?

객체의 레이어 구분을 위해 main/java/racingcar 패키지 하위로 view, domain을 패키지를 나눠주시고 각 객체들을 이동시켜보세요.

int max = -1;

for (Car car : carCollection.getCars()) {
int lastPosition = car.getLastPosition();

if (max < lastPosition) {
max = lastPosition;
}
}
return max;
}

private String getWinner(CarCollection carCollection, int biggestLastPosition) {
Copy link

Choose a reason for hiding this comment

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

이 메소드도 도메인 객체로 이동해야하는 로직과 View에 남아있어야 할 로직이 섞여 있는 것 같아요 🥲

ArrayList<String> winnerList = new ArrayList<>();

for (Car car : carCollection.getCars()) {
int lastPosition = car.getLastPosition();

if (lastPosition == biggestLastPosition) {
winnerList.add(car.getName());
}
}

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

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

import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.jupiter.api.Assertions.*;

class CarTest {
@Test
void proceedOrStop함수는_0_또는_1을_반환한다() {
// Given
Car car = new Car("pobi", Arrays.asList(1, 2, 3, 4, 4));

// Then
assertThat(car.proceedOrStop()).isIn(0, 1);
assertThat(car.proceedOrStop()).isIn(0, 1);
assertThat(car.proceedOrStop()).isIn(0, 1);
assertThat(car.proceedOrStop()).isIn(0, 1);
assertThat(car.proceedOrStop()).isIn(0, 1);
Comment on lines +18 to +22
Copy link

Choose a reason for hiding this comment

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

proceedOrStop 메소드는 내부에 랜덤값때문에 어떤 값이 나올지 몰라서 지금 같은 테스트 코드가 나온 것 같아요.
인터페이스를 이용해 랜덤숫자를 만드는 로직을 Car객체에서 분리해보세요 :)

}

@Test
void Car_객체_테스트() {
// Given
Car car = new Car("pobi", Arrays.asList(1, 2, 3, 4, 5));

// Then
assertThat(car.getName()).isEqualTo("pobi");
assertThat(car.getPositions()).isEqualTo(Arrays.asList(1, 2, 3, 4, 5));
}

@Test
void 자동차_이름은_5자를_초과할_수_없다() {
assertThatIllegalArgumentException().isThrownBy(() -> {
Car car = new Car("pobibi", Arrays.asList(1, 2, 3, 4, 5));
}).withMessage("자동차 이름은 5자를 초과할 수 없다.");
}
}
27 changes: 27 additions & 0 deletions src/test/java/racingcar/InputViewTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package racingcar;

import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;

import static org.assertj.core.api.Assertions.assertThat;

class InputViewTest {

public InputView inputView;

@Test
void 문자_입력_받기() {
// Given
String testInput = "pobi,crong,honux\n5\n";
ByteArrayInputStream in = new ByteArrayInputStream(testInput.getBytes());
System.setIn(in);

// When
inputView = new InputView();

// Then
assertThat(inputView.getCarNames()).isEqualTo("pobi,crong,honux");
assertThat(inputView.getTrialCount()).isEqualTo(5);
}
}
Loading