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

5단계 - 자동차 경주(리팩토링) #6079

Merged
merged 6 commits into from
Mar 22, 2025
Merged
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
20 changes: 9 additions & 11 deletions src/main/java/step3/Main.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
package step3;


import step3.game.Car;
import step3.game.CarFactory;
import step3.game.GameHistory;
import step3.game.RacingGame;
import step3.game.*;
import step3.random.DefaultRandomStrategy;
import step3.util.InputView;
import step3.util.OutputView;

import java.util.List;

public class Main {
private static final String CAR_NAME_MESSAGE = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).";
private static final String MOVE_COUNT_MESSAGE = "시도할 회수는 몇 회 인가요?";
public static void main(String[] args) {
String carNames = InputView.inputString(CAR_NAME_MESSAGE);
int moveCount = InputView.inputInt(MOVE_COUNT_MESSAGE);
String carNames = InputView.inputCarNames();
int moveCount = InputView.inputMove();

List<Car> cars = CarFactory.createCars(carNames);
RacingGame game = new RacingGame(moveCount, new DefaultRandomStrategy(), cars);
List<List<GameHistory>> result = game.start();
OutputView.printResult(result, game.getCurrentWinners());

RacingGame game = new RacingGame(moveCount, cars);
List<GameRound> result = game.start(new DefaultRandomStrategy());

OutputView.printResult(result, Winners.findWinners(result));
}
}
35 changes: 31 additions & 4 deletions src/main/java/step3/game/Car.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
package step3.game;

import java.util.Objects;

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

private static final int POSSIBLE_MOVE_CONDITION = 4;
private static final int MAX_MOVE_CONDITION = 9;
private static final int MIN_MOVE_CONDITION = 0;
private int position = 0;

public Car(String name) {
this(name, 0);
}
public Car(String name, int position) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("자동차 이름은 비어있거나 null이 될 수 없습니다.");
}
if (name.length() > 5) {
throw new IllegalArgumentException("자동차 이름은 5자 이하여야 합니다.");
}
this.name = name;
this.position = position;
}

public void move(int number) {
if (number > MAX_MOVE_CONDITION || number < MIN_MOVE_CONDITION) {
throw new IllegalArgumentException("움직일 수 있는 조건을 벗어났습니다. 값 : " + number);
Expand All @@ -25,11 +31,32 @@ public void move(int number) {
position++;
}
}
public boolean isSamePosition(int other) {
return position == other;
}

public int getPosition() {
return position;
public int max(int other) {
return Math.max(position, other);
}
public Car copy() {
return new Car(name, position);
}
public String getName() {
return name;
}
public int getPosition() {
return position;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return position == car.position && Objects.equals(name, car.name);
}

@Override
public int hashCode() {
return Objects.hash(name, position);
}
}
11 changes: 10 additions & 1 deletion src/main/java/step3/game/CarFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@

public class CarFactory {
private final static String CAR_NAME_DELIMITER = ",";
private final static int MINIMUM_CAR_COUNT = 2;
public static List<Car> createCars(String names) {
if (!names.contains(CAR_NAME_DELIMITER)) {
throw new IllegalArgumentException("자동차 이름은 쉼표(,)를 기준으로 구분해야 합니다.");
}
String[] carNames = names.split(CAR_NAME_DELIMITER);
if (carNames.length < 2) {
if (carNames.length < MINIMUM_CAR_COUNT) {
throw new IllegalArgumentException("자동차는 최소 2대 이상이어야 합니다.");
}
List<Car> cars = new ArrayList<>();
Expand All @@ -19,4 +20,12 @@ public static List<Car> createCars(String names) {
}
return cars;
}

public static List<Car> copyCars(List<Car> cars) {
List<Car> copyCar = new ArrayList<>();
for (int i = 0; i < cars.size(); i++) {
copyCar.add(cars.get(i).copy());
}
return copyCar;
}
}
20 changes: 0 additions & 20 deletions src/main/java/step3/game/GameHistory.java

This file was deleted.

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

import java.util.List;
import java.util.Objects;

public class GameRound {
private final List<Car> result;

public GameRound(List<Car> cars) {
this.result = CarFactory.copyCars(cars);
}

public List<Car> getRound() {
return result;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

GameRound that = (GameRound) o;

return Objects.equals(result, that.result);
}

@Override
public int hashCode() {
return result.hashCode();
}
}
50 changes: 8 additions & 42 deletions src/main/java/step3/game/RacingGame.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,28 @@
import step3.random.RandomStrategy;

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

public class RacingGame {
private List<Car> cars;
private final List<List<GameHistory>> result = new ArrayList<>();
private final List<Car> cars;
private final List<GameRound> result = new ArrayList<>();
private final int moveCount;
private final RandomStrategy random;

public RacingGame(int moveCount, RandomStrategy random, List<Car> cars) {
this.random = random;
public RacingGame(int moveCount, List<Car> cars) {
this.moveCount = moveCount;
this.cars = cars;
}

public List<List<GameHistory>> start() {
public List<GameRound> start(RandomStrategy random) {
for (int i = 0; i < moveCount; i++) {
moveCars();
saveCurrentCarPositions(i);
moveCars(random);
}
return result;
}

private void moveCars() {
for (Car car : cars) {
car.move(random());
}
}

private int random() {
return random.generateRandomValue();
}

private void saveCurrentCarPositions(int index) {
result.add(new ArrayList<>());
private void moveCars(RandomStrategy random) {
for (Car car : cars) {
result.get(index).add(new GameHistory(car.getName(), car.getPosition()));
}
}

public List<String> getCurrentWinners() {
if (result.isEmpty()) {
return Collections.emptyList();
}

List<String> winners = new ArrayList<>();
int max = 0;
for (GameHistory history : result.get(result.size() - 1)) {
if (history.getPosition() > max) {
max = history.getPosition();
winners.clear();
winners.add(history.getName());
} else if (history.getPosition() == max) {
winners.add(history.getName());
}
car.move(random.generateRandomValue());
}
return winners;
result.add(new GameRound(cars));
}
}
26 changes: 26 additions & 0 deletions src/main/java/step3/game/Winners.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package step3.game;

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

public class Winners {
public static List<String> findWinners(List<GameRound> cars) {
List<Car> finalRound = cars.get(cars.size() - 1).getRound();
return filterWinners(finalRound, findMaxPosition(finalRound));
}

private static List<String> filterWinners(List<Car> cars, int maxPosition) {
return cars.stream()
.filter(car -> car.isSamePosition(maxPosition))
.map(Car::getName)
.collect(Collectors.toList());
}

private static int findMaxPosition(List<Car> cars) {
int maxPosition = 0;
for (Car car : cars) {
maxPosition = car.max(maxPosition);
}
return maxPosition;
}
Comment on lines +6 to +25
Copy link

Choose a reason for hiding this comment

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

Winner가 GameRound를 다루는 static한 함수만 가지고 있을게 아니라

일급컬렉션으로 설계해보면 더 좋을것 같습니다. :)

}
10 changes: 6 additions & 4 deletions src/main/java/step3/util/InputView.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@

public class InputView {
private static final Scanner scanner = new Scanner(System.in);
private static final String CAR_NAME_MESSAGE = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).";
private static final String MOVE_COUNT_MESSAGE = "시도할 회수는 몇 회 인가요?";

public InputView() {}

public static int inputInt(String message) {
System.out.println(message);
public static int inputMove() {
System.out.println(MOVE_COUNT_MESSAGE);
return scanner.nextInt();
}

public static String inputString(String message) {
System.out.println(message);
public static String inputCarNames() {
System.out.println(CAR_NAME_MESSAGE);
return scanner.nextLine();
}
}
13 changes: 7 additions & 6 deletions src/main/java/step3/util/OutputView.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package step3.util;

import step3.game.GameHistory;
import step3.game.Car;
import step3.game.GameRound;

import java.util.List;

public class OutputView {

public static void printResult(List<List<GameHistory>> result, List<String> winners) {
public static void printResult(List<GameRound> result, List<String> winners) {
System.out.println("실행결과");
printHistories(result);
printWinners(winners);
}

private static void printHistories(List<List<GameHistory>> result) {
for (List<GameHistory> history : result) {
for (int i = 0; i < history.size(); i++) {
System.out.println(history.get(i).toString());
private static void printHistories(List<GameRound> result) {
for (GameRound round : result) {
for (Car car : round.getRound()) {
System.out.println(car.getName() + " : " + "-".repeat(car.getPosition()));
Comment on lines +17 to +19
Copy link

Choose a reason for hiding this comment

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

depth가 2인것 같네요~!

}
System.out.println();
}
Expand Down
12 changes: 12 additions & 0 deletions src/test/java/step3/CarFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,16 @@ public void createCarsExceptionTest2() {
}).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("자동차는 최소 2대 이상이어야 합니다.");
}

@Test
@DisplayName("자동차 copy가 정상적으로 이루어지는지 확인한다.")
public void copyCarsTest() {
// given
List<Car> cars = List.of(new Car("a", 1), new Car("b", 2), new Car("c", 3));
// when
List<Car> copyCars = CarFactory.copyCars(cars);

// then
assertThat(copyCars).isNotSameAs(cars);
}
}
26 changes: 23 additions & 3 deletions src/test/java/step3/CarTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvFileSource;
import org.junit.jupiter.params.provider.CsvSource;
import step3.game.Car;

Expand All @@ -12,12 +11,12 @@

public class CarTest {
@ParameterizedTest
@CsvSource(value = {"3:0", "4:1", "5:1"}, delimiter = ':')
@CsvSource(value = {"3:0", "4:1"}, delimiter = ':')
@DisplayName("움직임이 4 미만일 경우, 자동차는 움직이지 않아야 한다.")
void carMoveTest(int number, int expected) {
Car car = new Car("car1");
car.move(number);
assertThat(car.getPosition()).isEqualTo(expected);
assertThat(car.isSamePosition(expected)).isTrue();
}

@Test
Expand Down Expand Up @@ -74,4 +73,25 @@ void carNameLengthTest() {
assertThat(car.getName()).isEqualTo("hihi");
}

@Test
@DisplayName("자동차 isSamePosition 메서드가 정상적으로 작동하는지 확인한다.")
void carIsSamePositionTest() {
Car car = new Car("car1", 1);
assertThat(car.isSamePosition(1)).isTrue();
}

@Test
@DisplayName("자동차 max 메서드가 정상적으로 작동하는지 확인한다.")
void carMaxTest() {
Car car = new Car("car1", 1);
assertThat(car.max(2)).isEqualTo(2);
}

@Test
@DisplayName("자동차 copy 메서드가 정상적으로 작동하는지 확인한다.")
void carCopyTest() {
Car car = new Car("car1", 1);
Car copyCar = car.copy();
assertThat(copyCar).isNotSameAs(car);
}
}
Loading