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

๐Ÿš€ 4๋‹จ๊ณ„ - ์ž๋™์ฐจ ๊ฒฝ์ฃผ(์šฐ์Šน์ž) #6061

Open
wants to merge 9 commits into
base: kjy2844
Choose a base branch
from
Prev Previous commit
Next Next commit
refactor : ํ…Œ์ŠคํŠธ์šฉ ์ƒ์„ฑ์ž ์ œ๊ฑฐ
kjy2844 committed Mar 20, 2025
commit 778c6c37413593a83bc9bb0893015386107dc378
23 changes: 10 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -95,19 +95,16 @@ ResultView : ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅ
- [x] `Game` ํด๋ž˜์Šค ์ˆ˜์ •
- [x] ๊ฒฝ์ฃผ ์ข…๋ฃŒ ํ›„ ์šฐ์Šน์ž ์ถœ๋ ฅ ๋กœ์ง ์ถ”๊ฐ€

5. **ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€**

- [ ] `RaceTest`์— ์šฐ์Šน์ž ํŒ์ • ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
- [ ] ๋‹จ์ผ ์šฐ์Šน์ž ์ผ€์ด์Šค
- [ ] ๋‹ค์ค‘ ์šฐ์Šน์ž ์ผ€์ด์Šค
- [ ] `InputViewTest` ์ถ”๊ฐ€
- [ ] ์ž๋™์ฐจ ์ด๋ฆ„ ์ž…๋ ฅ ํ…Œ์ŠคํŠธ
- [ ] ์ž˜๋ชป๋œ ์ž…๋ ฅ ์ฒ˜๋ฆฌ ํ…Œ์ŠคํŠธ

6. **๋ฆฌํŒฉํ† ๋ง ๊ณ ๋ ค์‚ฌํ•ญ**
- [ ] `Car` ํด๋ž˜์Šค์— `Position` ๊ฐ’ ๊ฐ์ฒด ๋„์ž… ๊ฒ€ํ† 
- [ ] `CarName` ๊ฐ’ ๊ฐ์ฒด ๋„์ž… ๊ฒ€ํ† 
- [ ] `CarStatus` DTO ๋„์ž… ๊ฒ€ํ†  (์ด๋ฆ„๊ณผ ์œ„์น˜๋ฅผ ํ•จ๊ป˜ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด)
### ๊ฐœ๋ฐœ ๊ตฌ์กฐ

- **Game**: ๊ฒŒ์ž„์˜ ์ „์ฒด ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ ๋ฐ ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜ ์กฐ์ •
- **Race**: ๊ฒฝ์ฃผ ์ง„ํ–‰ ์ƒํƒœ ๊ด€๋ฆฌ, ์ž๋™์ฐจ ์ด๋™ ์ฒ˜๋ฆฌ, ์šฐ์Šน์ž ํŒ์ •
- **GameSettings**: ๊ฒŒ์ž„ ์„ค์ •๊ฐ’(์ž๋™์ฐจ ์ด๋ฆ„, ๋ผ์šด๋“œ ์ˆ˜) ๊ฒ€์ฆ ๋ฐ ๊ด€๋ฆฌ
- **Car**: ์ž๋™์ฐจ์˜ ๊ธฐ๋ณธ ์†์„ฑ(์ด๋ฆ„, ์œ„์น˜) ๊ด€๋ฆฌ
- **InputView**: ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ฒ˜๋ฆฌ ๋ฐ ๊ฒ€์ฆ
- **ResultView**: ๊ฒŒ์ž„ ์ง„ํ–‰ ์ƒํƒœ ๋ฐ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
- **CarStatus**: ์ž๋™์ฐจ์˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ๋ถˆ๋ณ€ ๊ฐ์ฒด๋กœ ํ‘œํ˜„
- **MoveStrategy**: ์ž๋™์ฐจ ์ด๋™ ์ „๋žต ์ •์˜ (RandomMoveStrategy, FixedMoveStrategy)

### ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์š”๊ตฌ์‚ฌํ•ญ

Empty file removed src/main/java/.gitkeep
Empty file.
16 changes: 2 additions & 14 deletions src/main/java/Car.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
public class Car {

private static final int MOVEMENT_THRESHOLD = 4;
private final String name;
private int position = 0;

@@ -9,13 +8,6 @@ public Car(String name) {
this.name = name.trim();
}

// ํ…Œ์ŠคํŠธ์šฉ ์ƒ์„ฑ์ž
Car(String name, int position) {
validateName(name);
this.name = name.trim();
this.position = position;
}

private void validateName(String name) {
if (name == null || name.trim().isBlank()) {
throw new IllegalArgumentException("Name cannot be blank");
@@ -25,12 +17,8 @@ private void validateName(String name) {
}
}

public void move(int seed) {
if (seed < 0 || seed > 9) {
throw new IllegalArgumentException("Invalid seed: " + seed);
}

if (seed >= MOVEMENT_THRESHOLD) {
public void move(boolean shouldMove) {
if (shouldMove) {
position++;
}
}
4 changes: 4 additions & 0 deletions src/main/java/MoveStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@FunctionalInterface
public interface MoveStrategy {
boolean shouldMove();
}
16 changes: 7 additions & 9 deletions src/main/java/Race.java
Original file line number Diff line number Diff line change
@@ -4,24 +4,22 @@

public class Race {

private static final Random random = new Random();
private final MoveStrategy moveStrategy;
private final int totalRounds;
private final List<Car> cars;
private int currentRound = 0;

public Race(GameSettings settings) {
this(settings, new RandomMoveStrategy());
}

public Race(GameSettings settings, MoveStrategy moveStrategy) {
this.totalRounds = settings.getRoundCount();
this.cars = new ArrayList<>();
for (String carName : settings.getCarNames()) {
this.cars.add(new Car(carName));
}
}

// ํ…Œ์ŠคํŠธ์šฉ ์ƒ์„ฑ์ž
Race(List<Car> cars, int totalRounds) {
this.cars = new ArrayList<>(cars);
this.totalRounds = totalRounds;
this.currentRound = totalRounds; // ๊ฒฝ์ฃผ๊ฐ€ ๋๋‚œ ์ƒํƒœ๋กœ ์„ค์ •
this.moveStrategy = moveStrategy;
}

public List<CarStatus> getCarStatuses() {
@@ -35,7 +33,7 @@ public List<CarStatus> getCarStatuses() {
public void runRound() {
validateRaceInProgress();
for (Car car : cars) {
car.move(random.nextInt(10));
car.move(moveStrategy.shouldMove());
}
currentRound++;
}
11 changes: 11 additions & 0 deletions src/main/java/RandomMoveStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import java.util.Random;

public class RandomMoveStrategy implements MoveStrategy {
private static final int MOVE_THRESHOLD = 4;
private final Random random = new Random();

@Override
public boolean shouldMove() {
return random.nextInt(10) >= MOVE_THRESHOLD;
}
}
17 changes: 4 additions & 13 deletions src/test/java/CarTest.java
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

class CarTest {

@@ -35,19 +34,11 @@ void carNameCannotExceed5Characters() {
}

@ParameterizedTest
@DisplayName("random๊ฐ’์ด 4 ์ด์ƒ์ผ ๊ฒฝ์šฐ์— ์ž๋™์ฐจ์˜ ์œ„์น˜๋Š” 1 ์ถ”๊ฐ€๋˜๊ณ , 4 ๋ฏธ๋งŒ์ผ ๊ฒฝ์šฐ ์œ„์น˜๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค")
@CsvSource({"0, 0", "1, 0", "2, 0", "3, 0", "4, 1", "5, 1", "6, 1", "7, 1", "8, 1", "9, 1"})
void carMovesAccordingToRandomValue(int seed, int expectedPosition) {
@DisplayName("shouldMove๊ฐ€ true์ผ ๊ฒฝ์šฐ ์ž๋™์ฐจ์˜ ์œ„์น˜๋Š” 1 ์ถ”๊ฐ€๋˜๊ณ , false์ผ ๊ฒฝ์šฐ ์œ„์น˜๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค")
@CsvSource({"false, 0", "true, 1"})
void carMovesAccordingToShouldMove(boolean shouldMove, int expectedPosition) {
Car car = new Car("MyCar");
car.move(seed);
car.move(shouldMove);
assertThat(car.getPosition()).isEqualTo(expectedPosition);
}

@ParameterizedTest
@DisplayName("์ด๋™์„ ์œ„ํ•œ ์ˆซ์ž๋Š” 0์—์„œ 9 ์‚ฌ์ด์—ฌ์•ผ ํ•œ๋‹ค")
@ValueSource(ints = {-1, 10})
void carMoveVariableIsBetween0And9(int invalidSeed) {
Car car = new Car("MyCar");
assertThatThrownBy(() -> car.move(invalidSeed)).isInstanceOf(IllegalArgumentException.class).hasMessage("Invalid seed: " + invalidSeed);
}
}
41 changes: 29 additions & 12 deletions src/test/java/RaceTest.java
Original file line number Diff line number Diff line change
@@ -59,14 +59,14 @@ void getWinnersBeforeRaceFinishThrowsError() {
@DisplayName("๋‹จ๋… ์šฐ์Šน์ž๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ์ž๋™์ฐจ๋งŒ ๋ฐ˜ํ™˜ํ•œ๋‹ค")
void getSingleWinner() {
// given
List<Car> cars = List.of(
new Car("car1", 5),
new Car("car2", 3),
new Car("car3", 4)
);
Race race = new Race(cars, 1);
String[] carNames = {"car1", "car2", "car3"};
GameSettings settings = new GameSettings(carNames, 3);
Race race = new Race(settings, new FixedMoveStrategy(new boolean[]{true, false, false}));

// when
race.runRound();
race.runRound();
race.runRound();
List<CarStatus> winners = race.getWinners();

// then
@@ -78,18 +78,35 @@ void getSingleWinner() {
@DisplayName("๊ณต๋™ ์šฐ์Šน์ž๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋ชจ๋“  ์šฐ์Šน์ž๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค")
void getMultipleWinners() {
// given
List<Car> cars = List.of(
new Car("car1", 5),
new Car("car2", 5),
new Car("car3", 3)
);
Race race = new Race(cars, 1);
String[] carNames = {"car1", "car2", "car3"};
GameSettings settings = new GameSettings(carNames, 3);
Race race = new Race(settings, new FixedMoveStrategy(new boolean[]{true, true, false}));

// when
race.runRound();
race.runRound();
race.runRound();
List<CarStatus> winners = race.getWinners();

// then
assertThat(winners).hasSize(2);
assertThat(winners).extracting("name").containsExactlyInAnyOrder("car1", "car2");
}

private static class FixedMoveStrategy implements MoveStrategy {
private final boolean[] moves;
private int index = 0;

FixedMoveStrategy(boolean[] moves) {
this.moves = moves;
}

@Override
public boolean shouldMove() {
if (index >= moves.length) {
index = 0;
}
return moves[index++];
}
}
}