diff --git a/README.md b/README.md index 977f65d2710..42ce4221437 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,11 @@ * 우승자는 차량 이름으로 표시한다. * ResultView에서 우승자 목록을 출력한다. * 우승자가 여러명일 경우 쉼표(,)로 구분하여 표시한다. + +### Step5 +* 아래 3가지 개발 원칙을 적용해본다 + * 모든 원시값과 문자열을 포장한다 + * 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. + * 한 줄에 점을 하나만 찍는다 + * 일급 콜렉션을 쓴다 + * 도메인 객체를 테스트가 가능하도록 수정 \ No newline at end of file diff --git a/src/main/java/race/CarList.java b/src/main/java/race/CarList.java new file mode 100644 index 00000000000..21aa30726d0 --- /dev/null +++ b/src/main/java/race/CarList.java @@ -0,0 +1,42 @@ +package race; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +public class CarList { + private final List cars; + + public CarList() { + this(new ArrayList<>()); + } + + public CarList(List cars) { + this.cars = cars; + } + + public void add(RacingCar car) { + cars.add(car); + } + + public void moveWithRandom(Random random) { + for (RacingCar car : cars) { + car.moveWithSeed(random.nextInt()); + } + } + + public List getList() { + return cars; + } + + public List getWinners() { + cars.sort(Collections.reverseOrder()); + RacingCar winner = cars.get(0); + + return cars.stream() + .filter(car -> car.compareTo(winner) == 0) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/race/CarName.java b/src/main/java/race/CarName.java new file mode 100644 index 00000000000..a588b07b601 --- /dev/null +++ b/src/main/java/race/CarName.java @@ -0,0 +1,15 @@ +package race; + +public class CarName { + private final String name; + + public CarName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/src/main/java/race/DefaultPositionPrinter.java b/src/main/java/race/DefaultPositionPrinter.java deleted file mode 100644 index d2ca103f7d9..00000000000 --- a/src/main/java/race/DefaultPositionPrinter.java +++ /dev/null @@ -1,8 +0,0 @@ -package race; - -public class DefaultPositionPrinter implements PositionPrinter { - @Override - public void printPosition(String carName, int position) { - System.out.println("-".repeat(position)); - } -} diff --git a/src/main/java/race/DefaultRandomNumberGenerator.java b/src/main/java/race/DefaultRandomNumberGenerator.java deleted file mode 100644 index ef44f095354..00000000000 --- a/src/main/java/race/DefaultRandomNumberGenerator.java +++ /dev/null @@ -1,12 +0,0 @@ -package race; - -import java.util.Random; - -public class DefaultRandomNumberGenerator implements RandomNumberGenerator { - private final Random random = new Random(); - - @Override - public int generate() { - return random.nextInt(10); // 0~9 사이 랜덤 값 생성 - } -} diff --git a/src/main/java/race/FixedNumberGenerator.java b/src/main/java/race/FixedNumberGenerator.java deleted file mode 100644 index a4369f3cc90..00000000000 --- a/src/main/java/race/FixedNumberGenerator.java +++ /dev/null @@ -1,14 +0,0 @@ -package race; - -public class FixedNumberGenerator implements RandomNumberGenerator { - private final int fixedValue; - - public FixedNumberGenerator(int fixedValue) { - this.fixedValue = fixedValue; - } - - @Override - public int generate() { - return fixedValue; - } -} diff --git a/src/main/java/race/Position.java b/src/main/java/race/Position.java new file mode 100644 index 00000000000..8578cab588a --- /dev/null +++ b/src/main/java/race/Position.java @@ -0,0 +1,42 @@ +package race; + +import java.util.Objects; + +public class Position implements Comparable { + private int value; + + public Position() { + this(0); + } + + public Position(int value) { + this.value = value; + } + + public Position move() { + this.value++; + return this; + } + + public int getValue() { + return this.value; + } + + @Override + public int compareTo(Position o) { + return Integer.compare(this.value, o.value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Position position = (Position) obj; + return value == position.value; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/src/main/java/race/PositionPrinter.java b/src/main/java/race/PositionPrinter.java deleted file mode 100644 index 724a9e38190..00000000000 --- a/src/main/java/race/PositionPrinter.java +++ /dev/null @@ -1,5 +0,0 @@ -package race; - -public interface PositionPrinter { - void printPosition(String carNumber, int position); -} diff --git a/src/main/java/race/RacingCar.java b/src/main/java/race/RacingCar.java index d8de295bde7..1d2f2d26782 100644 --- a/src/main/java/race/RacingCar.java +++ b/src/main/java/race/RacingCar.java @@ -2,20 +2,17 @@ public class RacingCar implements Comparable { private static final int MAX_NAME_LENGTH = 5; - private final String name; - private int position; - private final RandomNumberGenerator randomNumberGenerator; - private final PositionPrinter positionPrinter; + private static final int MOVE_CRITERIA = 4; + private final CarName name; + private final Position position; - private RacingCar(String name, RandomNumberGenerator randomNumberGenerator, PositionPrinter positionPrinter) { - this.name = name; - this.randomNumberGenerator = randomNumberGenerator; - this.positionPrinter = positionPrinter; + public RacingCar(String name) { + this(name, 0); } - @Override - public int compareTo(RacingCar other) { - return Integer.compare(this.position, other.position); + public RacingCar(String name, int position) { + this.name = new CarName(name); + this.position = new Position(position); } public static boolean validateName(String carName) { @@ -23,26 +20,26 @@ public static boolean validateName(String carName) { } private boolean shouldMove(int num) { - return num >= 4; + return num >= MOVE_CRITERIA; } - public int move() { - int randomValue = randomNumberGenerator.generate(); - if (shouldMove(randomValue)) { - position++; + public Position moveWithSeed(int seed) { + if (shouldMove(seed)) { + return this.position.move(); } - return position; + return this.position; } - public void printPosition() { - positionPrinter.printPosition(name, position); + public CarName getName() { + return this.name; } - static RacingCar create(String racingName, RandomNumberGenerator randomNumberGenerator, PositionPrinter positionPrinter) { - return new RacingCar(racingName, randomNumberGenerator, positionPrinter); + public Position getPosition() { + return this.position; } - public String getName() { - return name; + @Override + public int compareTo(RacingCar o) { + return this.position.compareTo(o.position); } } diff --git a/src/main/java/race/RacingCarFactory.java b/src/main/java/race/RacingCarFactory.java deleted file mode 100644 index 37332e19bc7..00000000000 --- a/src/main/java/race/RacingCarFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package race; - -public class RacingCarFactory { - private static final RandomNumberGenerator DEFAULT_RANDOM_NUMBER_GENERATOR = new DefaultRandomNumberGenerator(); - private static final PositionPrinter DEFAULT_POSITION_VIEWER = new DefaultPositionPrinter(); - - public static RacingCar create(String carName) { - return RacingCar.create(carName, DEFAULT_RANDOM_NUMBER_GENERATOR, DEFAULT_POSITION_VIEWER); - } - - public static RacingCar create(String carName, RandomNumberGenerator randomNumberGenerator) { - return RacingCar.create(carName, randomNumberGenerator, DEFAULT_POSITION_VIEWER); - } - - public static RacingCar create(String carName, PositionPrinter positionPrinter) { - return RacingCar.create(carName, DEFAULT_RANDOM_NUMBER_GENERATOR, positionPrinter); - } - - public static RacingCar create(String carName, RandomNumberGenerator randomNumberGenerator, PositionPrinter positionPrinter) { - return RacingCar.create(carName, randomNumberGenerator, positionPrinter); - } -} diff --git a/src/main/java/race/RacingTrack.java b/src/main/java/race/RacingTrack.java index 7422a6d01e8..14c3b65b9de 100644 --- a/src/main/java/race/RacingTrack.java +++ b/src/main/java/race/RacingTrack.java @@ -1,18 +1,15 @@ package race; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Random; public class RacingTrack { - private final int maxCarCount; - private final int maxAttemptCount; - private final List cars; + private final TrackCondition trackCondition; + private final CarList cars; public RacingTrack(int maxCarCount, int maxAttemptCount) { - this.maxCarCount = maxCarCount; - this.maxAttemptCount = maxAttemptCount; - this.cars = new ArrayList<>(); + this.trackCondition = new TrackCondition(maxCarCount, maxAttemptCount); + this.cars = new CarList(); } public void startRace(String[] carNames, int numOfAttempts) { @@ -20,44 +17,27 @@ public void startRace(String[] carNames, int numOfAttempts) { ResultView.printRaceStartMessage(); for (int i = 0; i < numOfAttempts; i++) { - moveAndShowCars(); - ResultView.printRaceStatus(cars); - } - } - - // getWinners() 메서드 테스틀 위하여 추가한 생성자 - public void startRace(List cars, int numOfAttempts) { - this.cars.addAll(cars); - - ResultView.printRaceStartMessage(); - for (int i = 0; i < numOfAttempts; i++) { - moveAndShowCars(); + cars.moveWithRandom(new Random()); ResultView.printRaceStatus(cars); } } private void setupCars(String[] carNames) { for (String name : carNames) { - cars.add(RacingCarFactory.create(name, ResultView.createPositionPrinter())); - } - } - - private void moveAndShowCars() { - for (RacingCar car : cars) { - car.move(); + cars.add(new RacingCar(name)); } } public boolean validateCarCount(int num) { - return num >= 1 && num <= this.maxCarCount; + return this.trackCondition.validateCarCount(num); } public boolean validateAttemptCount(int num) { - return num >= 1 && num <= this.maxAttemptCount; + return this.trackCondition.validateAttemptCount(num); } public boolean validateCarNames(String[] names) { - if (names.length < 1 || names.length > this.maxCarCount) { + if (!this.trackCondition.validateCarCount(names.length)) { return false; } for (String name : names) { @@ -67,13 +47,6 @@ public boolean validateCarNames(String[] names) { } public List getWinners() { - cars.sort(Collections.reverseOrder()); - List winners = new ArrayList<>(); - winners.add(cars.get(0)); - - for (int i = 1; i < cars.size(); i++) { - if (cars.get(i).compareTo(cars.get(0)) == 0) winners.add(cars.get(i)); - } - return winners; + return this.cars.getWinners(); } } diff --git a/src/main/java/race/RandomNumberGenerator.java b/src/main/java/race/RandomNumberGenerator.java deleted file mode 100644 index 06cfbe62e23..00000000000 --- a/src/main/java/race/RandomNumberGenerator.java +++ /dev/null @@ -1,5 +0,0 @@ -package race; - -public interface RandomNumberGenerator { - int generate(); -} diff --git a/src/main/java/race/ResultView.java b/src/main/java/race/ResultView.java index a27cc3be9f0..bafbf6cc87d 100644 --- a/src/main/java/race/ResultView.java +++ b/src/main/java/race/ResultView.java @@ -3,22 +3,13 @@ import java.util.List; public class ResultView { - public static PositionPrinter createPositionPrinter() { - return new PositionPrinter() { - @Override - public void printPosition(String carName, int position) { - System.out.println(String.format("%-5s", carName) + " : " + "-".repeat(position)); - } - }; - } - public static void printRaceStartMessage() { System.out.println("\n### Racing Start!!! ###\n"); } - public static void printRaceStatus(List cars) { - for (RacingCar car : cars) { - car.printPosition(); + public static void printRaceStatus(CarList cars) { + for (RacingCar car : cars.getList()) { + System.out.println(String.format("%-5s", car.getName().toString()) + " : " + "-".repeat(car.getPosition().getValue())); } System.out.println(); } @@ -26,7 +17,7 @@ public static void printRaceStatus(List cars) { public static void printRaceWinners(List cars) { String[] winners = new String[cars.size()]; for (RacingCar car : cars) { - winners[cars.indexOf(car)] = car.getName(); + winners[cars.indexOf(car)] = car.getName().toString(); } System.out.println(String.join(", ", winners) + "가 최종 우승했습니다."); } diff --git a/src/main/java/race/TrackCondition.java b/src/main/java/race/TrackCondition.java new file mode 100644 index 00000000000..1fff6a3a440 --- /dev/null +++ b/src/main/java/race/TrackCondition.java @@ -0,0 +1,19 @@ +package race; + +public class TrackCondition { + private final int maxCarCount; + private final int maxAttemptCount; + + public TrackCondition(int maxCarCount, int maxAttemptCount) { + this.maxCarCount = maxCarCount; + this.maxAttemptCount = maxAttemptCount; + } + + public boolean validateCarCount(int num) { + return num >= 1 && num <= this.maxCarCount; + } + + public boolean validateAttemptCount(int num) { + return num >= 1 && num <= this.maxAttemptCount; + } +} diff --git a/src/test/java/race/DefaultRandomNumberGeneratorTest.java b/src/test/java/race/DefaultRandomNumberGeneratorTest.java deleted file mode 100644 index 26b4072d18e..00000000000 --- a/src/test/java/race/DefaultRandomNumberGeneratorTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package race; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -public class DefaultRandomNumberGeneratorTest { - @Test - void getRandomNumber() { - DefaultRandomNumberGenerator defaultRandomNumberGenerator = new DefaultRandomNumberGenerator(); - assertThat(defaultRandomNumberGenerator.generate()).isBetween(0, 9); - } -} diff --git a/src/test/java/race/RacingCarTest.java b/src/test/java/race/RacingCarTest.java index 6fe139e58f7..b761c069c78 100644 --- a/src/test/java/race/RacingCarTest.java +++ b/src/test/java/race/RacingCarTest.java @@ -8,29 +8,16 @@ public class RacingCarTest { @Order(10) @Test - void move() { - RacingCar car = RacingCarFactory.create("1", new FixedNumberGenerator(5)); - assertThat(car.move()).isEqualTo(1); - assertThat(car.move()).isEqualTo(2); - } - - @Order(20) - @Test - void not_move() { - RacingCar car = RacingCarFactory.create("1", new FixedNumberGenerator(2)); - assertThat(car.move()).isEqualTo(0); - assertThat(car.move()).isEqualTo(0); + void moveWithSeed() { + RacingCar car = new RacingCar("1"); + assertThat(car.moveWithSeed(5)).isEqualTo(new Position(1)); + assertThat(car.moveWithSeed(2)).isEqualTo(new Position(1)); } @Order(30) @Test - void valid_car_name() { + void validate_car_name() { assertThat(RacingCar.validateName("test")).isTrue(); - } - - @Order(40) - @Test - void invalid_car_name() { assertThat(RacingCar.validateName("")).isFalse(); assertThat(RacingCar.validateName("aaaaaaa")).isFalse(); } diff --git a/src/test/java/race/RacingTrackTest.java b/src/test/java/race/RacingTrackTest.java index 601739b0bab..3abf69c29c0 100644 --- a/src/test/java/race/RacingTrackTest.java +++ b/src/test/java/race/RacingTrackTest.java @@ -9,40 +9,25 @@ public class RacingTrackTest { @Test - void valid_car_count() { + void validate_car_count() { RacingTrack racingTrack = new RacingTrack(10, 10); assertThat(racingTrack.validateCarCount(5)).isTrue(); - } - - @Test - void invalid_car_count() { - RacingTrack racingTrack = new RacingTrack(10, 10); assertThat(racingTrack.validateCarCount(0)).isFalse(); assertThat(racingTrack.validateCarCount(15)).isFalse(); } @Test - void valid_attempt_count() { + void validate_attempt_count() { RacingTrack racingTrack = new RacingTrack(10, 10); assertThat(racingTrack.validateAttemptCount(5)).isTrue(); - } - - @Test - void invalid_attempt_count() { - RacingTrack racingTrack = new RacingTrack(10, 10); assertThat(racingTrack.validateAttemptCount(0)).isFalse(); assertThat(racingTrack.validateAttemptCount(15)).isFalse(); } @Test - void valid_car_names() { + void validate_car_names() { RacingTrack racingTrack = new RacingTrack(10, 10); assertThat(racingTrack.validateCarNames(new String[]{"test1", "test2"})).isTrue(); - } - - @Test - void invalid_car_names() { - RacingTrack racingTrack = new RacingTrack(10, 10); assertThat(racingTrack.validateCarNames(new String[]{"", "test2"})).isFalse(); assertThat(racingTrack.validateCarNames(new String[]{"testtesttest", "test2"})).isFalse(); assertThat(racingTrack.validateCarNames(new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"})).isFalse(); @@ -50,25 +35,21 @@ void invalid_car_names() { @Test void one_winner_test() { - RacingCar car1 = RacingCarFactory.create("1", new FixedNumberGenerator(5)); - RacingCar car2 = RacingCarFactory.create("2", new FixedNumberGenerator(3)); - List cars = new ArrayList<>(List.of(car1, car2)); - RacingTrack racingTrack = new RacingTrack(10, 10); - racingTrack.startRace(cars, 5); - List winners = racingTrack.getWinners(); + RacingCar car1 = new RacingCar("1", 4); + RacingCar car2 = new RacingCar("2", 2); + CarList cars = new CarList(new ArrayList<>(List.of(car1, car2))); + List winners = cars.getWinners(); assertThat(winners.size()).isEqualTo(1); assertThat(winners.get(0)).isEqualTo(car1); } @Test void two_winner_test() { - RacingCar car1 = RacingCarFactory.create("1", new FixedNumberGenerator(5)); - RacingCar car2 = RacingCarFactory.create("2", new FixedNumberGenerator(3)); - RacingCar car3 = RacingCarFactory.create("3", new FixedNumberGenerator(5)); - List cars = new ArrayList<>(List.of(car1, car2, car3)); - RacingTrack racingTrack = new RacingTrack(10, 10); - racingTrack.startRace(cars, 5); - List winners = racingTrack.getWinners(); + RacingCar car1 = new RacingCar("1", 4); + RacingCar car2 = new RacingCar("2", 2); + RacingCar car3 = new RacingCar("3", 4); + CarList cars = new CarList(new ArrayList<>(List.of(car1, car2, car3))); + List winners = cars.getWinners(); assertThat(winners.size()).isEqualTo(2); assertThat(winners.get(0)).isEqualTo(car1); assertThat(winners.get(1)).isEqualTo(car3);