diff --git a/.gitignore b/.gitignore index 6c01878138..d8469256ea 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ out/ ### VS Code ### .vscode/ + +### mac ### +.DS_Store diff --git a/src/main/java/calculator/Calculator.java b/src/main/java/calculator/Calculator.java index 4729f72e7f..88c5687a5f 100644 --- a/src/main/java/calculator/Calculator.java +++ b/src/main/java/calculator/Calculator.java @@ -1,44 +1,53 @@ package calculator; +import calculator.view.Output; + import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Calculator { - private static final int WRONG_VALUE_RESULT = 0; private static final int CUSTOM_DELIMITER_PART = 1; private static final int NUMBER_PART = 2; + public static final String EMPTY_VALUE = "0"; + + public int splitAndSum(final String text) { + String calculatorText = replaceEmpty(text); + return sum(getSplitNumbers(calculatorText)); + } - public int splitAndSum(String text) { + private String replaceEmpty(final String text) { if (isEmpty(text)) { - return WRONG_VALUE_RESULT; + Output.emptyValueOfSum(); + return EMPTY_VALUE; } - return sum(getSplitNumbers(text)); + return text; } - private boolean isEmpty(String text) { + private boolean isEmpty(final String text) { return text == null || text.trim().isEmpty(); } - private int[] getSplitNumbers(String text) { + private int[] getSplitNumbers(final String text) { String delimiter = ",|:"; + String splitText = text; Matcher m = Pattern.compile("//(.)\n(.*)").matcher(text); if (m.find()) { delimiter += "|" + m.group(CUSTOM_DELIMITER_PART); - text = m.group(NUMBER_PART); + splitText = m.group(NUMBER_PART); } - return stringToIntArray(text.split(delimiter)); + return stringToIntArray(splitText.split(delimiter)); } - private int[] stringToIntArray(String[] strings) { + private int[] stringToIntArray(final String[] strings) { return Arrays.stream(strings) .filter(this::isValidNumber) .map(Integer::parseInt) .mapToInt(Integer::intValue).toArray(); } - private boolean isValidNumber(String string) { + private boolean isValidNumber(final String string) { if (isEmpty(string)) { return false; } @@ -48,11 +57,11 @@ private boolean isValidNumber(String string) { return true; } - private boolean isNumber(String string) { + private boolean isNumber(final String string) { return string.matches("[+-]?\\d*?(\\.\\d+)?"); } - private int sum(int[] numbers) { + private int sum(final int[] numbers) { if (numbers.length == 1) { return numbers[0]; } @@ -64,7 +73,7 @@ private int sum(int[] numbers) { return result; } - private void checkNegative(int number) { + private void checkNegative(final int number) { if (number < 0) { throw new RuntimeException("자연수를 입력해주세요."); } diff --git a/src/main/java/calculator/view/Output.java b/src/main/java/calculator/view/Output.java new file mode 100644 index 0000000000..b0416d8211 --- /dev/null +++ b/src/main/java/calculator/view/Output.java @@ -0,0 +1,11 @@ +package calculator.view; + +public class Output { + private static void info(final String message) { + System.out.println("[INFO] " + message); + } + + public static void emptyValueOfSum() { + info("빈 값은 0으로 바뀌어 계산됩니다."); + } +} diff --git a/src/main/java/racingcar/Application.java b/src/main/java/racingcar/Application.java index f7c555617a..40af895a51 100644 --- a/src/main/java/racingcar/Application.java +++ b/src/main/java/racingcar/Application.java @@ -1,6 +1,6 @@ package racingcar; -import racingcar.service.RacingCarGame; +import racingcar.controller.RacingCarGame; public class Application { public static void main(String[] args) { diff --git a/src/main/java/racingcar/controller/RacingCarGame.java b/src/main/java/racingcar/controller/RacingCarGame.java new file mode 100644 index 0000000000..0338d6d81b --- /dev/null +++ b/src/main/java/racingcar/controller/RacingCarGame.java @@ -0,0 +1,61 @@ +package racingcar.controller; + +import racingcar.domain.Attempt; +import racingcar.domain.Cars; +import racingcar.domain.RoundResult; +import racingcar.view.Input; +import racingcar.view.Output; + +import java.util.List; + +public class RacingCarGame { + private Cars cars; + private Attempt attempt; + private final RoundResult roundResult = new RoundResult(); + + private final Input input; + + public RacingCarGame() { + input = new Input(); + } + + public void run() { + init(); + round(); + win(); + } + + @Override + public String toString() { + return "- cars\n" + cars + "- " + attempt; + } + + private void init() { + cars = createCars(input.carName()); + attempt = createAttempt(input.attempt()); + } + + private Cars createCars(final String carName) { + return new Cars(carName); + } + + private Attempt createAttempt(final String attempt) { + return new Attempt(attempt); + } + + private void round() { + Output.resultTitle(); + + int nowAttempt = 0; + while (!attempt.isSame(nowAttempt++)) { + cars.play(roundResult); + roundResult.roundEnd(); + } + Output.result(roundResult); + } + + private void win() { + List winners = cars.findWinners(); + Output.showResult(winners); + } +} diff --git a/src/main/java/racingcar/domain/Attempt.java b/src/main/java/racingcar/domain/Attempt.java index c6cb7fbfe4..f42c0b0191 100644 --- a/src/main/java/racingcar/domain/Attempt.java +++ b/src/main/java/racingcar/domain/Attempt.java @@ -3,43 +3,45 @@ public class Attempt { private final int attempt; - public Attempt(int attempt) { + public Attempt(final String attempt) { + this(stringToInt(attempt)); + } + + public Attempt(final int attempt) { if (isNegative(attempt)) { throw new IllegalArgumentException("시도횟수는 1이상의 수를 입력해주세요."); } this.attempt = attempt; } - - public Attempt(String attempt) { - checkValid(attempt); - this.attempt = Integer.parseInt(attempt); - } - public boolean isSame(int nowAttempt) { + public boolean isSame(final int nowAttempt) { return attempt == nowAttempt; } - private void checkValid(String attempt) { + @Override + public String toString() { + return "attempt : " + attempt; + } + + private static int stringToInt(final String text) { + checkValid(text); + return Integer.parseInt(text); + } + + private static void checkValid(final String attempt) { if (attempt.isBlank()) { throw new IllegalArgumentException("반복 횟수는 %s일 수 없습니다."); } if (!isNumber(attempt)) { throw new IllegalArgumentException("시도횟수는 숫자를 입력해주세요."); } - if (isNegative(attempt)) { - throw new IllegalArgumentException("시도횟수는 1이상의 수를 입력해주세요."); - } } - private boolean isNumber(String string) { + private static boolean isNumber(final String string) { return string.matches("[+-]?\\d*(\\.\\d+)?"); } - private boolean isNegative(int number) { + private static boolean isNegative(final int number) { return number <= 0; } - - private boolean isNegative(String string) { - return Integer.parseInt(string) <= 0; - } } diff --git a/src/main/java/racingcar/domain/Car.java b/src/main/java/racingcar/domain/Car.java index 76f04b300d..cf45a53276 100644 --- a/src/main/java/racingcar/domain/Car.java +++ b/src/main/java/racingcar/domain/Car.java @@ -6,21 +6,17 @@ public class Car implements Comparable { private final String name; private int position = 0; - public Car(String name) { - checkValid(name); + public Car(final String name) { + checkValidName(name); this.name = name; } - public void drive(boolean directing) { - if (directing) { + public void drive(final Movable movable) { + if (movable.isMoving()) { move(); } } - public boolean isSamePosition(Car other) { - return this.position == other.position; - } - public String getName() { return name; } @@ -29,17 +25,26 @@ public int getPosition() { return position; } + public boolean isSamePosition(final Car other) { + return this.position == other.position; + } + @Override - public int compareTo(Car car) { + public int compareTo(final Car car) { return this.position - car.position; } - private void checkValid(String names) { + @Override + public String toString() { + return "name : " + name + ", position : " + position; + } + + private void checkValidName(final String names) { checkBlank(names); checkNameLength(names); } - private void checkBlank(String name) { + private void checkBlank(final String name) { String text = "자동차 이름은 %s일 수 없습니다."; if (name == null) { throw new NullPointerException(String.format(text, "null")); @@ -49,7 +54,7 @@ private void checkBlank(String name) { } } - private void checkNameLength(String name) { + private void checkNameLength(final String name) { if (!(name.trim().length() <= CAR_LENGTH_LIMIT)) { throw new IllegalArgumentException("자동차의 이름은 " + CAR_LENGTH_LIMIT + "글자를 초과할 수 없습니다."); } diff --git a/src/main/java/racingcar/domain/Cars.java b/src/main/java/racingcar/domain/Cars.java index 20a3b70f1c..ecccf6a75d 100644 --- a/src/main/java/racingcar/domain/Cars.java +++ b/src/main/java/racingcar/domain/Cars.java @@ -1,37 +1,34 @@ package racingcar.domain; -import racingcar.view.Output; - import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; -import java.util.Set; import java.util.stream.Collectors; public class Cars { private static final String DELIMITER = ","; private static final int CAR_LIMIT = 2; - private static final int DRIVE_FLAG = 3; - private List cars = new ArrayList<>(); + private final List cars; - public Cars(String names) { + public Cars(final String names) { this(names.split(DELIMITER)); } - public Cars(String[] names) { - checkValid(names); - for (String name : names) { - cars.add(createCar(name)); - } + public Cars(final String[] names) { + this(stringArrayToCarList(names)); + } + + public Cars(final List cars) { + checkValid(cars); + this.cars = new ArrayList<>(cars); } - public void play() { + public void play(final RoundResult roundResult) { + Movable movable = new DecisionToMoving(); for (Car car : cars) { - car.drive(hasNext()); + car.drive(movable); + roundResult.save(car.getName(), car.getPosition()); } - Output.roundResult(cars); } public List findWinners() { @@ -39,34 +36,46 @@ public List findWinners() { return findSamePositionCar(maxPositionCar); } - private void checkValid(String[] names) { - if (!isCars(names)) { - throw new IllegalArgumentException("자동차를 두 개 이상 입력해주세요."); - } - if (isDuplicated(names)) { - throw new IllegalArgumentException("자동차 이름을 모두 다르게 입력해주세요."); + @Override + public String toString() { + String result = ""; + for (Car car : cars) { + result += car + "\n"; } - } - private boolean isCars(String[] names) { - return names.length >= CAR_LIMIT; + return result; } - private boolean isDuplicated(String[] names) { - Set carNames = new HashSet<>(Arrays.asList(names)); - return carNames.size() != names.length; + private static List stringArrayToCarList(final String[] names) { + List tempCars = new ArrayList<>(); + for (String name : names) { + tempCars.add(createCar(name)); + } + return tempCars; } - private Car createCar(String name) { + private static Car createCar(final String name) { return new Car(name.trim()); } - private boolean hasNext() { - return generate() > DRIVE_FLAG; + private void checkValid(final List cars) { + if (!isCars(cars)) { + throw new IllegalArgumentException("자동차를 두 개 이상 입력해주세요."); + } + if (isDuplicated(cars)) { + throw new IllegalArgumentException("자동차 이름을 모두 다르게 입력해주세요."); + } + } + + private boolean isCars(final List cars) { + return cars.size() >= CAR_LIMIT; } - private int generate() { - return (int)(Math.random() * 100) % 10; + private boolean isDuplicated(final List cars) { + return cars.stream() + .map(Car::getName) + .distinct() + .count() != cars.size(); } private Car findMaxPositionCar() { @@ -75,7 +84,7 @@ private Car findMaxPositionCar() { .orElseThrow(() -> new NoSuchElementException("max 값을 찾을 수 없습니다.")); } - private List findSamePositionCar(Car target) { + private List findSamePositionCar(final Car target) { return cars.stream() .filter(car -> car.isSamePosition(target)) .map(Car::getName) diff --git a/src/main/java/racingcar/domain/DecisionToMoving.java b/src/main/java/racingcar/domain/DecisionToMoving.java new file mode 100644 index 0000000000..5130ee71d7 --- /dev/null +++ b/src/main/java/racingcar/domain/DecisionToMoving.java @@ -0,0 +1,10 @@ +package racingcar.domain; + +public class DecisionToMoving implements Movable { + private static final int MOVE_FLAG = 3; + + @Override + public boolean isMoving() { + return (int)(Math.random() * 100) % 10 > MOVE_FLAG; + } +} diff --git a/src/main/java/racingcar/domain/Movable.java b/src/main/java/racingcar/domain/Movable.java new file mode 100644 index 0000000000..d7dff56860 --- /dev/null +++ b/src/main/java/racingcar/domain/Movable.java @@ -0,0 +1,6 @@ +package racingcar.domain; + +public interface Movable { + + boolean isMoving(); +} diff --git a/src/main/java/racingcar/domain/RoundResult.java b/src/main/java/racingcar/domain/RoundResult.java new file mode 100644 index 0000000000..566bd2ccc3 --- /dev/null +++ b/src/main/java/racingcar/domain/RoundResult.java @@ -0,0 +1,28 @@ +package racingcar.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RoundResult { + private static final String STEP = "-"; + + private final List result = new ArrayList<>(); + + public void save(final String name, final int position) { + result.add(name + " : " + STEP.repeat(position)); + } + + public void roundEnd() { + result.add(""); + } + + public List getResult() { + return Collections.unmodifiableList(result); + } + + @Override + public String toString() { + return String.join("\n", result); + } +} diff --git a/src/main/java/racingcar/service/RacingCarGame.java b/src/main/java/racingcar/service/RacingCarGame.java deleted file mode 100644 index 260064ae31..0000000000 --- a/src/main/java/racingcar/service/RacingCarGame.java +++ /dev/null @@ -1,44 +0,0 @@ -package racingcar.service; - -import racingcar.domain.Attempt; -import racingcar.domain.Cars; -import racingcar.view.Input; -import racingcar.view.Output; - -import java.util.List; - -public class RacingCarGame { - private Cars cars; - private Attempt attempt; - - private final Input input; - - public RacingCarGame() { - input = new Input(); - } - - public void run() { - init(); - round(); - win(); - } - - private void init() { - cars = input.carName(); - attempt = input.attempt(); - } - - private void round() { - int nowAttempt = 0; - - while (!attempt.isSame(nowAttempt)) { - cars.play(); - nowAttempt++; - } - } - - private void win() { - List winners = cars.findWinners(); - Output.showResult(winners); - } -} diff --git a/src/main/java/racingcar/view/Input.java b/src/main/java/racingcar/view/Input.java index 87790ccdf4..e1ac706f0d 100644 --- a/src/main/java/racingcar/view/Input.java +++ b/src/main/java/racingcar/view/Input.java @@ -1,33 +1,32 @@ package racingcar.view; -import racingcar.domain.Attempt; -import racingcar.domain.Cars; - import java.util.Scanner; public class Input { private final Scanner scan = new Scanner(System.in); - public Cars carName() { + public String carName() { try { Output.getCarName(); - String inputValue = scan.nextLine(); - return new Cars(inputValue); + return inputText(); } catch (IllegalArgumentException e) { Output.errorMessage(e.getMessage()); return carName(); } } - public Attempt attempt() { + public String attempt() { try { Output.getAttempt(); - String inputValue = scan.nextLine(); - return new Attempt(inputValue); + return inputText(); } catch (IllegalArgumentException e) { Output.errorMessage(e.getMessage()); return attempt(); } } + + private String inputText() { + return scan.nextLine(); + } } diff --git a/src/main/java/racingcar/view/Output.java b/src/main/java/racingcar/view/Output.java index 995e4a1271..f3edc53c94 100644 --- a/src/main/java/racingcar/view/Output.java +++ b/src/main/java/racingcar/view/Output.java @@ -1,17 +1,11 @@ package racingcar.view; -import racingcar.domain.Car; +import racingcar.domain.RoundResult; import java.util.List; public class Output { - private static final String STEP = "-"; - private static StringBuilder stringBuilder = new StringBuilder(); - - private Output() { - } - - public static void errorMessage(String message) { + public static void errorMessage(final String message) { System.out.println("[ERROR] " + message); } @@ -23,22 +17,20 @@ public static void getAttempt() { System.out.println("시도할 회수는 몇회인가요?"); } - public static void roundResult(List cars) { - for (Car car : cars) { - stringBuilder.append(car.getName()).append(" : "); - stringBuilder.append(STEP.repeat(car.getPosition())); - stringBuilder.append("\n"); - } - stringBuilder.append("\n"); + public static void resultTitle() { + newLine(); + System.out.println("실행 결과"); + } + + public static void result(final RoundResult round) { + System.out.println(String.join("\n", round.getResult())); } - public static void allRoundResult() { - System.out.println("\n실행 결과"); - System.out.print(stringBuilder); + public static void newLine() { + System.out.println(); } - public static void showResult(List winners) { - allRoundResult(); + public static void showResult(final List winners) { System.out.println(String.join(", ", winners) + "가 최종 우승했습니다."); } } diff --git a/src/test/java/racingcar/domain/CarTest.java b/src/test/java/racingcar/domain/CarTest.java index 2b852a2ccc..d55c1449c3 100644 --- a/src/test/java/racingcar/domain/CarTest.java +++ b/src/test/java/racingcar/domain/CarTest.java @@ -39,7 +39,9 @@ void car_drive_true() { Car carPositionZero = new Car("zero"); // position: 0 Car carDriveTrue = new Car("drive"); - carDriveTrue.drive(true); + Movable movable = () -> true; + + carDriveTrue.drive(movable); assertFalse(carDriveTrue.isSamePosition(carPositionZero)); } @@ -50,7 +52,9 @@ void car_drive_false() { Car carPositionZero = new Car("zero"); // position: 0 Car carDriveFalse = new Car("drive"); - carDriveFalse.drive(false); + Movable movable = () -> false; + + carDriveFalse.drive(movable); assertTrue(carDriveFalse.isSamePosition(carPositionZero)); } diff --git a/src/test/java/racingcar/domain/CarsTest.java b/src/test/java/racingcar/domain/CarsTest.java index 260196358a..6f0fb9f0a6 100644 --- a/src/test/java/racingcar/domain/CarsTest.java +++ b/src/test/java/racingcar/domain/CarsTest.java @@ -2,98 +2,113 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; import java.util.List; -import java.util.NoSuchElementException; -import java.util.stream.Collectors; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class CarsTest { - private List cars; - - @BeforeEach - void init() { - String[] names = "car1,car2,car3".split(","); - cars = new ArrayList<>(); - for (String name : names) { - cars.add(new Car(name.trim())); - } - } - @DisplayName("자동차 개수 테스트") + @DisplayName("문자열로 입력 시 자동차 개수 테스트") @Test - void cars_count_test() { + void cars_count_string() { assertThatThrownBy(() -> { Cars cars = new Cars("pobi"); }).isInstanceOf(IllegalArgumentException.class); } - @DisplayName("자동차 이름 중복 테스트") + @DisplayName("Car 객체로 입력 시 자동차 개수 테스트") @Test - void cars_name_duplicated() { + void cars_count() { assertThatThrownBy(() -> { - Cars cars = new Cars("pobi,jun,pobi"); + List carList = new ArrayList<>(); + Car car = new Car("pobi"); + carList.add(car); + + Cars cars = new Cars(carList); }).isInstanceOf(IllegalArgumentException.class); } - @DisplayName("자동차 랜덤 숫자 범위 확인") + @DisplayName("문자열로 입력 시 자동차 이름 중복 테스트") @Test - void cars_random_range() { - //given, when - int value = generate(); - //then - assertTrue(value > -1 && value < 11); + void cars_name_duplicated_string() { + assertThatThrownBy(() -> { + Cars cars = new Cars("pobi,jun,pobi"); + }).isInstanceOf(IllegalArgumentException.class); } - @DisplayName("이동거리 최댓값을 가지는 Car 찾기") + @DisplayName("Car 객체로 입력 시 자동차 이름 중복 테스트") @Test - void cars_max_position_car() { - //given - setPositionCars(); - //when - Car maxMoveCar = findMaxPositionCar(); - //then - assertTrue(maxMoveCar.getPosition() == 2); + void cars_name_duplicated() { + assertThatThrownBy(() -> { + List carList = new ArrayList<>(); + Car car = new Car("pobi"); + Car car2 = new Car("pobi"); + carList.add(car); + carList.add(car2); + + Cars cars = new Cars(carList); + }).isInstanceOf(IllegalArgumentException.class); } - @DisplayName("공동 승리") + @DisplayName("우승자 1명") @Test - void cars_winners() { - //given - setPositionCars(); - //when - Car maxMoveCar = findMaxPositionCar(); - List winners = findSamePositionCar(maxMoveCar); - //then - assertThat(winners).containsExactly("car1", "car2"); - } + void cars_winner() { + Cars cars = new Cars(get_winner_cars()); + + List winners = cars.findWinners(); - void setPositionCars() { - int[] moveList = {0, 0, 1, 1, 2}; - for (int i : moveList) { - cars.get(i).drive(true); - } + assertThat(winners).containsExactly("car2"); } - private int generate() { - return (int)(Math.random() * 100) % 10; + private List get_winner_cars() { + List cars = new ArrayList<>(); + Car car1 = new Car("car1"); // position : 2 + Car car2 = new Car("car2"); // position : 3 + Car car3 = new Car("car3"); // position : 1 + + Movable movable = () -> true; + car1.drive(movable); + car1.drive(movable); + car2.drive(movable); + car2.drive(movable); + car2.drive(movable); + car3.drive(movable); + + cars.add(car1); + cars.add(car2); + cars.add(car3); + return cars; } - private Car findMaxPositionCar() { - return cars.stream() - .max(Car::compareTo) - .orElseThrow(() -> new NoSuchElementException("max 값을 찾을 수 없습니다.")); + @DisplayName("우승자 2명") + @Test + void cars_winners() { + Cars cars = new Cars(get_cars_winners()); + + List winners = cars.findWinners(); + + assertThat(winners).containsExactly("car1", "car2"); } - private List findSamePositionCar(Car target) { - return cars.stream() - .filter(car -> car.isSamePosition(target)) - .map(Car::getName) - .collect(Collectors.toList()); + private List get_cars_winners() { + List cars = new ArrayList<>(); + Car car1 = new Car("car1"); // position : 2 + Car car2 = new Car("car2"); // position : 2 + Car car3 = new Car("car3"); // position : 1 + + Movable movable = () -> true; + car1.drive(movable); + car1.drive(movable); + car2.drive(movable); + car2.drive(movable); + car3.drive(movable); + + cars.add(car1); + cars.add(car2); + cars.add(car3); + return cars; } } \ No newline at end of file