diff --git a/src/main/java/edu/nextstep/camp/carracing/Car.java b/src/main/java/edu/nextstep/camp/carracing/Car.java deleted file mode 100644 index e776ff1808c..00000000000 --- a/src/main/java/edu/nextstep/camp/carracing/Car.java +++ /dev/null @@ -1,40 +0,0 @@ -package edu.nextstep.camp.carracing; - -public class Car { - private static final int MOVE_THRESHOLD = 4; - private static final String MOVE_SYMBOL = "-"; - - private final CarName name; - private int position; - - public Car(String name) { - this(name, 0); - } - - public Car(String name, int position) { - this.name = new CarName(name); - this.position = position; - } - - public void move(int number) { - if (number >= MOVE_THRESHOLD) { - position++; - } - } - - public String getCurrentPositionString() { - return String.format("%s : %s", name.getName(), MOVE_SYMBOL.repeat(position)); - } - - public boolean isMaxPosition(int position) { - return this.position == position; - } - - public CarName getName() { - return name; - } - - public int getPosition() { - return position; - } -} diff --git a/src/main/java/edu/nextstep/camp/carracing/CarRacing.java b/src/main/java/edu/nextstep/camp/carracing/CarRacing.java deleted file mode 100644 index e7fd55172ad..00000000000 --- a/src/main/java/edu/nextstep/camp/carracing/CarRacing.java +++ /dev/null @@ -1,53 +0,0 @@ -package edu.nextstep.camp.carracing; - -import java.util.ArrayList; -import java.util.List; - -import static edu.nextstep.camp.carracing.CarValidator.checkForDuplicateCarNames; -import static edu.nextstep.camp.carracing.InputView.getTryCount; -import static edu.nextstep.camp.carracing.InputView.inputCarNames; -import static edu.nextstep.camp.carracing.RandomNumberGenerator.generateRandomNumber; -import static edu.nextstep.camp.carracing.Winners.getWinners; - -public class CarRacing { - private static final int RANDOM_NUMBER_BOUND = 10; - - private CarRacing() { - throw new IllegalStateException("인스턴스 생성이 불가능한 클래스입니다."); - } - - public static void main(String[] args) { - racing(); - } - - public static void racing() { - List carNames = inputCarNames(); - checkForDuplicateCarNames(carNames); - List cars = initializeCars(carNames); - - int tryCount = getTryCount(); - - ResultView.printResultMessage(); - for (int i = 0; i < tryCount; i++) { - moveCars(cars); - ResultView.printCarStatus(cars); - } - - getWinners(cars); - } - - private static List initializeCars(List carNames) { - List cars = new ArrayList<>(); - for (String name : carNames) { - cars.add(new Car(name)); - } - return cars; - } - - private static void moveCars(List cars) { - for (Car car : cars) { - int randomNumber = generateRandomNumber(RANDOM_NUMBER_BOUND); - car.move(randomNumber); - } - } -} \ No newline at end of file diff --git a/src/main/java/edu/nextstep/camp/carracing/CarRacingApplication.java b/src/main/java/edu/nextstep/camp/carracing/CarRacingApplication.java new file mode 100644 index 00000000000..0517cc978ae --- /dev/null +++ b/src/main/java/edu/nextstep/camp/carracing/CarRacingApplication.java @@ -0,0 +1,26 @@ +package edu.nextstep.camp.carracing; + +import edu.nextstep.camp.carracing.domain.Cars; +import edu.nextstep.camp.carracing.util.RandomNumberGenerator; +import edu.nextstep.camp.carracing.view.ResultView; + +import java.util.List; + +import static edu.nextstep.camp.carracing.view.InputView.getTryCount; +import static edu.nextstep.camp.carracing.view.InputView.inputCarNames; + +public class CarRacingApplication { + public static void main(String[] args) { + List carNames = inputCarNames(); + int tryCount = getTryCount(); + + Cars cars = Cars.fromNames(carNames); + + ResultView.printResultMessage(); + for (int i = 0; i < tryCount; i++) { + cars.moveCars(new RandomNumberGenerator(10)); + ResultView.printCarsStatus(cars); + } + ResultView.printWinners(cars.getWinners()); + } +} diff --git a/src/main/java/edu/nextstep/camp/carracing/CarValidator.java b/src/main/java/edu/nextstep/camp/carracing/CarValidator.java deleted file mode 100644 index aa7d1a22748..00000000000 --- a/src/main/java/edu/nextstep/camp/carracing/CarValidator.java +++ /dev/null @@ -1,18 +0,0 @@ -package edu.nextstep.camp.carracing; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class CarValidator { - private CarValidator() { - throw new IllegalStateException("인스턴스 생성이 불가능한 클래스입니다."); - } - - public static void checkForDuplicateCarNames(List carNames) { - Set carNamesSet = new HashSet<>(carNames); - if (carNamesSet.size() != carNames.size()) { - throw new IllegalArgumentException("중복된 자동차 이름이 있습니다."); - } - } -} diff --git a/src/main/java/edu/nextstep/camp/carracing/RandomNumberGenerator.java b/src/main/java/edu/nextstep/camp/carracing/RandomNumberGenerator.java deleted file mode 100644 index 5c2010f2577..00000000000 --- a/src/main/java/edu/nextstep/camp/carracing/RandomNumberGenerator.java +++ /dev/null @@ -1,15 +0,0 @@ -package edu.nextstep.camp.carracing; - -import java.util.Random; - -public class RandomNumberGenerator { - private static final Random random = new Random(); - - private RandomNumberGenerator() { - throw new IllegalStateException("인스턴스 생성이 불가능한 클래스입니다."); - } - - public static int generateRandomNumber(int bound) { - return random.nextInt(bound); - } -} diff --git a/src/main/java/edu/nextstep/camp/carracing/Winners.java b/src/main/java/edu/nextstep/camp/carracing/Winners.java deleted file mode 100644 index d1ce66a8abd..00000000000 --- a/src/main/java/edu/nextstep/camp/carracing/Winners.java +++ /dev/null @@ -1,39 +0,0 @@ -package edu.nextstep.camp.carracing; - -import java.util.ArrayList; -import java.util.List; - -public class Winners { - private Winners() { - throw new IllegalStateException("인스턴스 생성이 불가능한 클래스입니다."); - } - - public static List getWinners(List cars) { - int maxPosition = getMaxPosition(cars); - List winners = getMaxPositionCars(cars, maxPosition); - ResultView.printWinners(winners); - return winners; - } - - private static int getMaxPosition(List cars) { - int maxPosition = 0; - for (Car car : cars) { - maxPosition = Math.max(maxPosition, car.getPosition()); - } - return maxPosition; - } - - private static List getMaxPositionCars(List cars, int maxPosition) { - List winners = new ArrayList<>(); - for (Car car : cars) { - addWinner(maxPosition, winners, car); - } - return winners; - } - - private static void addWinner(int maxPosition, List winners, Car car) { - if (car.isMaxPosition(maxPosition)) { - winners.add(car.getName().getName()); - } - } -} diff --git a/src/main/java/edu/nextstep/camp/carracing/domain/Car.java b/src/main/java/edu/nextstep/camp/carracing/domain/Car.java new file mode 100644 index 00000000000..d0f03a17d06 --- /dev/null +++ b/src/main/java/edu/nextstep/camp/carracing/domain/Car.java @@ -0,0 +1,39 @@ +package edu.nextstep.camp.carracing.domain; + +public class Car { + private static final int MOVE_THRESHOLD = 4; + + private final CarName name; + private Position position; + + public Car(String name) { + this(name, new Position()); + } + + public Car(String name, Position position) { + this.name = new CarName(name); + this.position = position; + } + + public void move(int number) { + if (number >= MOVE_THRESHOLD) { + this.position = this.position.increment(); + } + } + + public boolean isMaxPosition(int position) { + return this.position.isSame(position); + } + + public int getMaxValue(int value) { + return this.position.max(value); + } + + public String getNameValue() { + return this.name.getName(); + } + + public Position getPosition() { + return this.position; + } +} diff --git a/src/main/java/edu/nextstep/camp/carracing/CarName.java b/src/main/java/edu/nextstep/camp/carracing/domain/CarName.java similarity index 56% rename from src/main/java/edu/nextstep/camp/carracing/CarName.java rename to src/main/java/edu/nextstep/camp/carracing/domain/CarName.java index dbb24bf0830..0e7d4f54d10 100644 --- a/src/main/java/edu/nextstep/camp/carracing/CarName.java +++ b/src/main/java/edu/nextstep/camp/carracing/domain/CarName.java @@ -1,4 +1,4 @@ -package edu.nextstep.camp.carracing; +package edu.nextstep.camp.carracing.domain; public class CarName { private static final int MAX_LENGTH = 5; @@ -6,17 +6,30 @@ public class CarName { private final String name; public CarName(String name) { - validateCarName(name); - this.name = name; - } - - private void validateCarName(String name) { if (name == null || name.isBlank()) { throw new IllegalArgumentException("자동차 이름은 null이거나 빈 문자열일 수 없습니다."); } if (name.length() > MAX_LENGTH) { throw new IllegalArgumentException("자동차 이름은 5자를 초과할 수 없습니다."); } + this.name = name.trim(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CarName)) { + return false; + } + CarName other = (CarName) o; + return name.equals(other.name); + } + + @Override + public int hashCode() { + return name.hashCode(); } public String getName() { diff --git a/src/main/java/edu/nextstep/camp/carracing/domain/Cars.java b/src/main/java/edu/nextstep/camp/carracing/domain/Cars.java new file mode 100644 index 00000000000..e1546a6b568 --- /dev/null +++ b/src/main/java/edu/nextstep/camp/carracing/domain/Cars.java @@ -0,0 +1,49 @@ +package edu.nextstep.camp.carracing.domain; + +import edu.nextstep.camp.carracing.util.NumberGenerator; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class Cars { + private final List values; + + public Cars(List cars) { + this.values = cars; + } + + public static Cars fromNames(List carNames) { + List cars = new ArrayList<>(); + for (String carName : carNames) { + cars.add(new Car(carName)); + } + return new Cars(cars); + } + + public void moveCars(NumberGenerator generator) { + for (Car car : this.values) { + car.move(generator.generateNumber()); + } + } + + private int getMaxPosition() { + int maxPosition = 0; + for (Car car : this.values) { + maxPosition = car.getMaxValue(maxPosition); + } + return maxPosition; + } + + public List getWinners() { + int winnerPosition = getMaxPosition(); + return this.values.stream() + .filter(car -> car.isMaxPosition(winnerPosition)) + .map(Car::getNameValue) + .collect(Collectors.toList()); + } + + public List getValues() { + return this.values; + } +} diff --git a/src/main/java/edu/nextstep/camp/carracing/domain/Position.java b/src/main/java/edu/nextstep/camp/carracing/domain/Position.java new file mode 100644 index 00000000000..28103f08cbc --- /dev/null +++ b/src/main/java/edu/nextstep/camp/carracing/domain/Position.java @@ -0,0 +1,51 @@ +package edu.nextstep.camp.carracing.domain; + +import java.util.Objects; + +public class Position { + private final int value; + + public Position() { + this(0); + } + + public Position(int value) { + if (value < 0) { + throw new IllegalArgumentException("위치 값에 음수가 들어갈 수 없습니다."); + } + this.value = value; + } + + public Position increment() { + return new Position(this.value + 1); + } + + public boolean isSame(int number) { + return this.value == number; + } + + public int max(int number) { + return Math.max(this.value, number); + } + + public int getValue() { + return this.value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Position)) { + return false; + } + Position other = (Position) o; + return this.value == other.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/main/java/edu/nextstep/camp/carracing/util/FixedNumberGenerator.java b/src/main/java/edu/nextstep/camp/carracing/util/FixedNumberGenerator.java new file mode 100644 index 00000000000..c4674d3923f --- /dev/null +++ b/src/main/java/edu/nextstep/camp/carracing/util/FixedNumberGenerator.java @@ -0,0 +1,14 @@ +package edu.nextstep.camp.carracing.util; + +public class FixedNumberGenerator implements NumberGenerator { + private final int number; + + public FixedNumberGenerator(int number) { + this.number = number; + } + + @Override + public int generateNumber() { + return number; + } +} diff --git a/src/main/java/edu/nextstep/camp/carracing/util/NumberGenerator.java b/src/main/java/edu/nextstep/camp/carracing/util/NumberGenerator.java new file mode 100644 index 00000000000..6e9ab41e06f --- /dev/null +++ b/src/main/java/edu/nextstep/camp/carracing/util/NumberGenerator.java @@ -0,0 +1,5 @@ +package edu.nextstep.camp.carracing.util; + +public interface NumberGenerator { + int generateNumber(); +} diff --git a/src/main/java/edu/nextstep/camp/carracing/util/RandomNumberGenerator.java b/src/main/java/edu/nextstep/camp/carracing/util/RandomNumberGenerator.java new file mode 100644 index 00000000000..afbb950cea1 --- /dev/null +++ b/src/main/java/edu/nextstep/camp/carracing/util/RandomNumberGenerator.java @@ -0,0 +1,17 @@ +package edu.nextstep.camp.carracing.util; + +import java.util.Random; + +public class RandomNumberGenerator implements NumberGenerator { + private final int bound; + private final Random random = new Random(); + + public RandomNumberGenerator(int bound) { + this.bound = bound; + } + + @Override + public int generateNumber() { + return random.nextInt(this.bound); + } +} diff --git a/src/main/java/edu/nextstep/camp/carracing/InputView.java b/src/main/java/edu/nextstep/camp/carracing/view/InputView.java similarity index 95% rename from src/main/java/edu/nextstep/camp/carracing/InputView.java rename to src/main/java/edu/nextstep/camp/carracing/view/InputView.java index 549eb6f2226..93d4d5e27e8 100644 --- a/src/main/java/edu/nextstep/camp/carracing/InputView.java +++ b/src/main/java/edu/nextstep/camp/carracing/view/InputView.java @@ -1,4 +1,4 @@ -package edu.nextstep.camp.carracing; +package edu.nextstep.camp.carracing.view; import java.util.List; import java.util.Scanner; diff --git a/src/main/java/edu/nextstep/camp/carracing/ResultView.java b/src/main/java/edu/nextstep/camp/carracing/view/ResultView.java similarity index 50% rename from src/main/java/edu/nextstep/camp/carracing/ResultView.java rename to src/main/java/edu/nextstep/camp/carracing/view/ResultView.java index 2f1eba6c1d1..1f8f1c0649b 100644 --- a/src/main/java/edu/nextstep/camp/carracing/ResultView.java +++ b/src/main/java/edu/nextstep/camp/carracing/view/ResultView.java @@ -1,27 +1,30 @@ -package edu.nextstep.camp.carracing; +package edu.nextstep.camp.carracing.view; + +import edu.nextstep.camp.carracing.domain.Car; +import edu.nextstep.camp.carracing.domain.Cars; import java.util.List; public class ResultView { private static final String RESULT_MESSAGE = "실행 결과"; + private static final String MOVE_SYMBOL = "-"; private ResultView() { throw new IllegalStateException("인스턴스 생성이 불가능한 클래스입니다."); } public static void printResultMessage() { - System.out.println(); - System.out.println(RESULT_MESSAGE); - } - - public static void printCarStatus(List cars) { - for (Car car : cars) { - System.out.println(car.getCurrentPositionString()); - } - System.out.println(); + System.out.println(System.lineSeparator() + RESULT_MESSAGE); } public static void printWinners(List winners) { System.out.println(String.join(", ", winners) + "가 최종 우승했습니다."); } + + public static void printCarsStatus(Cars cars) { + for (Car car : cars.getValues()) { + System.out.printf("%s : %s%s", car.getNameValue(), MOVE_SYMBOL.repeat(car.getPosition().getValue()), System.lineSeparator()); + } + System.out.println(); + } } diff --git a/src/test/java/edu/nextstep/camp/carracing/CarTest.java b/src/test/java/edu/nextstep/camp/carracing/CarTest.java index 209a96ef4d5..279af1d15f8 100644 --- a/src/test/java/edu/nextstep/camp/carracing/CarTest.java +++ b/src/test/java/edu/nextstep/camp/carracing/CarTest.java @@ -1,5 +1,6 @@ package edu.nextstep.camp.carracing; +import edu.nextstep.camp.carracing.domain.Car; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -7,8 +8,8 @@ class CarTest { @ParameterizedTest - @CsvSource(value = {"0,'myCar : '", "9,'myCar : -'"}) - void 숫자에_따른_차_이동_후_position_테스트(int number, String expectedPosition) { + @CsvSource(value = {"0,0", "9,1"}) + void 숫자에_따른_차_이동_후_position_테스트(int number, int expectedPosition) { // Given Car car = new Car("myCar"); @@ -16,6 +17,6 @@ class CarTest { car.move(number); // Then - assertThat(car.getCurrentPositionString()).isEqualTo(expectedPosition); + assertThat(car.getPosition().getValue()).isEqualTo(expectedPosition); } } diff --git a/src/test/java/edu/nextstep/camp/carracing/RandomNumberGeneratorTest.java b/src/test/java/edu/nextstep/camp/carracing/RandomNumberGeneratorTest.java deleted file mode 100644 index 45459d5e0dd..00000000000 --- a/src/test/java/edu/nextstep/camp/carracing/RandomNumberGeneratorTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package edu.nextstep.camp.carracing; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import static org.assertj.core.api.Assertions.assertThat; - -class RandomNumberGeneratorTest { - @ParameterizedTest - @ValueSource(ints = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) - void generateRandomNumber(int bound) { - assertThat(RandomNumberGenerator.generateRandomNumber(bound)).isBetween(0, bound - 1); - } -} diff --git a/src/test/java/edu/nextstep/camp/carracing/ResultViewTest.java b/src/test/java/edu/nextstep/camp/carracing/ResultViewTest.java deleted file mode 100644 index c749c11ada5..00000000000 --- a/src/test/java/edu/nextstep/camp/carracing/ResultViewTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package edu.nextstep.camp.carracing; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -class ResultViewTest { - private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - @BeforeEach - void setUp() { - System.setOut(new PrintStream(outputStream)); - } - - @Test - void 자동차의_현재_위치가_잘_출력되는지_테스트() { - // Given - List cars = List.of(new Car("car1", 2), new Car("car2", 1), new Car("car3", 3)); - - // When - ResultView.printCarStatus(cars); - - // Then - String expectedOutput = "car1 : --\ncar2 : -\ncar3 : ---\n\n"; - assertThat(outputStream.toString()).isEqualTo(expectedOutput); - } - - @Test - void 결과_메시지가_잘_출력되는지_테스트() { - // When - ResultView.printResultMessage(); - - // Then - assertThat(outputStream.toString()).isEqualTo("\n실행 결과\n"); - } -} diff --git a/src/test/java/edu/nextstep/camp/carracing/WinnersTest.java b/src/test/java/edu/nextstep/camp/carracing/WinnersTest.java index f8abd12079b..37070396928 100644 --- a/src/test/java/edu/nextstep/camp/carracing/WinnersTest.java +++ b/src/test/java/edu/nextstep/camp/carracing/WinnersTest.java @@ -1,5 +1,8 @@ package edu.nextstep.camp.carracing; +import edu.nextstep.camp.carracing.domain.Car; +import edu.nextstep.camp.carracing.domain.Cars; +import edu.nextstep.camp.carracing.domain.Position; import org.junit.jupiter.api.Test; import java.util.List; @@ -9,31 +12,23 @@ class WinnersTest { @Test void 우승자_출력_테스트() { - // Given - Car car1 = new Car("car1", 2); - Car car2 = new Car("car2", 1); - Car car3 = new Car("car3", 3); + Car car1 = new Car("car1", new Position(2)); + Car car2 = new Car("car2", new Position(1)); + Car car3 = new Car("car3", new Position(3)); + Cars cars = new Cars(List.of(car1, car2, car3)); - // When - List winners = Winners.getWinners(List.of(car1, car2, car3)); - - // Then - assertThat(winners).containsExactly("car3"); + assertThat(cars.getWinners()).containsExactly("car3"); } @Test void 우승자_여러명_출력_테스트() { - // Given - Car car1 = new Car("car1", 2); - Car car2 = new Car("car2", 1); - Car car3 = new Car("car3", 3); - Car car4 = new Car("car4", 3); - Car car5 = new Car("car5", 3); - - // When - List winners = Winners.getWinners(List.of(car1, car2, car3, car4, car5)); - - // Then - assertThat(winners).containsExactly("car3", "car4", "car5"); + Car car1 = new Car("car1", new Position(2)); + Car car2 = new Car("car2", new Position(1)); + Car car3 = new Car("car3", new Position(3)); + Car car4 = new Car("car4", new Position(3)); + Car car5 = new Car("car5", new Position(3)); + Cars cars = new Cars(List.of(car1, car2, car3, car4, car5)); + + assertThat(cars.getWinners()).containsExactly("car3", "car4", "car5"); } } diff --git a/src/test/java/edu/nextstep/camp/carracing/domain/CarsTest.java b/src/test/java/edu/nextstep/camp/carracing/domain/CarsTest.java new file mode 100644 index 00000000000..a5a92781c57 --- /dev/null +++ b/src/test/java/edu/nextstep/camp/carracing/domain/CarsTest.java @@ -0,0 +1,27 @@ +package edu.nextstep.camp.carracing.domain; + +import edu.nextstep.camp.carracing.util.FixedNumberGenerator; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class CarsTest { + @ParameterizedTest + @CsvSource(value = {"1,0", "4,1"}) + void 차량_이동_테스트(int number, int expectedPosition) { + // Given + Cars cars = new Cars(List.of(new Car("car1"), new Car("car2"))); + + // When + FixedNumberGenerator generator = new FixedNumberGenerator(number); + cars.moveCars(generator); + + // Then + for (Car car : cars.getValues()) { + assertThat(car.getPosition().getValue()).isEqualTo(expectedPosition); + } + } +} \ No newline at end of file diff --git a/src/test/java/edu/nextstep/camp/carracing/domain/PositionTest.java b/src/test/java/edu/nextstep/camp/carracing/domain/PositionTest.java new file mode 100644 index 00000000000..2ad8398f51a --- /dev/null +++ b/src/test/java/edu/nextstep/camp/carracing/domain/PositionTest.java @@ -0,0 +1,31 @@ +package edu.nextstep.camp.carracing.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PositionTest { + + @Test + void 음수_테스트() { + assertThatThrownBy(() -> new Position(-1)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("위치 값에 음수가 들어갈 수 없습니다."); + } + + @Test + void 값_동등성_테스트() { + Position position1 = new Position(5); + Position position2 = new Position(5); + + assertThat(position1).isEqualTo(position2); + } + + @Test + void 값_증가_테스트() { + Position position = new Position(1); + + assertThat(position.increment()).isEqualTo(new Position(2)); + } +} \ No newline at end of file