diff --git a/docs/README.md b/docs/README.md
index e69de29bb2d..897edaf2007 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -0,0 +1,50 @@
+# πͺ νλ‘μ νΈ κ°μ
+
+건λ μ μλ λ€λ¦¬λ₯Ό μμ±νκ³ , νλ μ΄μ΄κ° λ€λ¦¬λ₯Ό 건λλ κ²μμ ꡬννλ€.
+
+# π ꡬν κΈ°λ₯ λͺ©λ‘
+
+### κ²μ μμ 문ꡬλ₯Ό μΆλ ₯νλ κΈ°λ₯
+
+- [x] `λ€λ¦¬ 건λκΈ° κ²μμ μμν©λλ€.`λ₯Ό μΆλ ₯νλ€.
+
+### μλμΌλ‘ λ€λ¦¬λ₯Ό μμ±νλ κΈ°λ₯
+
+- [x] λ€λ¦¬μ κΈΈμ΄λ₯Ό μ
λ ₯νλ€.
+ - [x] `λ€λ¦¬μ κΈΈμ΄λ₯Ό μ
λ ₯ν΄μ£ΌμΈμ.`λ₯Ό μΆλ ₯νλ€.
+ - [x] λ€λ¦¬μ κΈΈμ΄λ₯Ό μ
λ ₯λ°λλ€.
+ - [x] λΉ λ¬Έμμ΄μ΄ μλμ κ²μ¦νλ€.
+ - [x] μ«μ μ
λ ₯μμ κ²μ¦νλ€.
+ - [x] 3 μ΄μ 20μ΄νμμ κ²μ¦νλ€.
+- [x] λ€λ¦¬λ₯Ό μμ±νλ€.
+ - [x] λ€λ¦¬μ κΈΈμ΄λ§νΌ 0κ³Ό 1 μ€ λ¬΄μμ κ°μ μμ±νλ€.
+ - [x] 0μΈ κ²½μ° μλ μΉΈ, 1μΈ κ²½μ° μ μΉΈμ 건λ μ μλ μΉΈμΌλ‘ μ μ₯νλ€.
+
+### νλ μ΄μ΄κ° λ€λ¦¬λ₯Ό μ΄λνλ κΈ°λ₯
+
+- [x] `μ΄λν μΉΈμ μ νν΄μ£ΌμΈμ. (μ: U, μλ: D)`λ₯Ό μΆλ ₯νλ€.
+- [x] νλ μ΄μ΄κ° μ΄λν μΉΈμ μ
λ ₯νλ€.
+ - [x] λΉ λ¬Έμμ΄μ΄ μλμ κ²μ¦νλ€.
+ - [x] U νΉμ Dμ μ
λ ₯μμ κ²μ¦νλ€.
+- [x] νλ μ΄μ΄κ° μ΄λν μΉΈμ΄ μ΄λν μ μλ μΉΈμΈμ§ κ²μ¬νλ€.
+ - [x] μ΄λν μ μλ μΉΈμ μ νν κ²½μ° Oλ₯Ό νμνλ€.
+ - [x] λͺ¨λ λ€λ¦¬λ₯Ό μ΄λν κ²½μ° κ²°κ³Όλ₯Ό λ°ννλ€.
+ - [x] μ΄λν μ μλ μΉΈμ μ νν κ²½μ°
+ - [x] μ¬μλ νΉμ μ’
λ£ μ¬λΆλ₯Ό μ ννλ€.
+ - [x] μ’
λ£λ₯Ό μ νν κ²½μ° κ²°κ³Όλ₯Ό λ°ννλ€.
+
+### κ²μμ μ’
λ£νλ κΈ°λ₯
+
+- [x] `μ΅μ’
κ²μ κ²°κ³Ό`λ₯Ό μΆλ ₯νλ€.
+- [x] μ΅μ’
μ μΌλ‘ λ§λ€μ΄μ§ λ€λ¦¬μ μνλ₯Ό μΆλ ₯νλ€.
+- [x] `κ²μ μ±κ³΅ μ¬λΆ: μ±κ³΅`μ κ°μ΄ κ²μ μ±κ³΅ μ¬λΆλ₯Ό μΆλ ₯νλ€.
+- [x] `μ΄ μλν νμ: 2`μ κ°μ΄ μ΄ μλν νμλ₯Ό μΆλ ₯νλ€.
+
+### μΉΈ μ΄λμ μ€ν¨ν κ²½μ° μ¬μλ νΉμ μ’
λ£ μ¬λΆλ₯Ό μ ννλ κΈ°λ₯
+
+- [x] `κ²μμ λ€μ μλν μ§ μ¬λΆλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ. (μ¬μλ: R, μ’
λ£: Q)`λ₯Ό μΆλ ₯νλ€.
+- [x] νλ μ΄μ΄λ μ¬μλ νΉμ μ’
λ£ μ¬λΆλ₯Ό μ
λ ₯νλ€.
+ - [x] λΉ λ¬Έμμ΄μ΄ μλμ κ²μ¦νλ€.
+ - [x] R νΉμ Qμ μ
λ ₯μμ κ²μ¦νλ€.
+- [x] μ¬μλλ₯Ό μ νν κ²½μ°, μ²μλΆν° λ€μ λ€λ¦¬λ₯Ό μ΄λνλ€.
+- [x] μ’
λ£λ₯Ό μ νν κ²½μ°, κ²μμ μ’
λ£νλ€.
diff --git a/src/main/java/bridge/Application.java b/src/main/java/bridge/Application.java
index 5cb72dfd3de..a0ba72cae0d 100644
--- a/src/main/java/bridge/Application.java
+++ b/src/main/java/bridge/Application.java
@@ -1,8 +1,42 @@
package bridge;
+import bridge.controller.BridgeController;
+import bridge.controller.BridgeGame;
+import bridge.controller.MoveController;
+import bridge.domain.BridgeMaker;
+import bridge.util.BridgeRandomNumberGenerator;
+import bridge.view.InputView;
+import bridge.view.OutputView;
+
public class Application {
public static void main(String[] args) {
- // TODO: νλ‘κ·Έλ¨ κ΅¬ν
+ InputView inputView = new InputView();
+ BridgeController bridgeController = getBridgeController(inputView);
+ MoveController moveController = getMoveController(inputView);
+ BridgeGame bridgeGame = getBridgeGame(bridgeController, moveController);
+ bridgeGame.run();
+ }
+
+ private static BridgeController getBridgeController(InputView inputView) {
+ return new BridgeController(
+ inputView,
+ new BridgeMaker(new BridgeRandomNumberGenerator())
+ );
+ }
+
+ private static MoveController getMoveController(InputView inputView) {
+ return new MoveController(
+ inputView,
+ new OutputView()
+ );
+ }
+
+ private static BridgeGame getBridgeGame(BridgeController bridgeController, MoveController moveController) {
+ return new BridgeGame(
+ new OutputView(),
+ bridgeController,
+ moveController
+ );
}
}
diff --git a/src/main/java/bridge/BridgeGame.java b/src/main/java/bridge/BridgeGame.java
deleted file mode 100644
index 834c1c8362b..00000000000
--- a/src/main/java/bridge/BridgeGame.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package bridge;
-
-/**
- * λ€λ¦¬ 건λκΈ° κ²μμ κ΄λ¦¬νλ ν΄λμ€
- */
-public class BridgeGame {
-
- /**
- * μ¬μ©μκ° μΉΈμ μ΄λν λ μ¬μ©νλ λ©μλ
- *
- * μ΄λμ μν΄ νμν λ©μλμ λ°ν νμ
(return type), μΈμ(parameter)λ μμ λ‘κ² μΆκ°νκ±°λ λ³κ²½ν μ μλ€.
- */
- public void move() {
- }
-
- /**
- * μ¬μ©μκ° κ²μμ λ€μ μλν λ μ¬μ©νλ λ©μλ
- *
- * μ¬μμμ μν΄ νμν λ©μλμ λ°ν νμ
(return type), μΈμ(parameter)λ μμ λ‘κ² μΆκ°νκ±°λ λ³κ²½ν μ μλ€.
- */
- public void retry() {
- }
-}
diff --git a/src/main/java/bridge/InputView.java b/src/main/java/bridge/InputView.java
deleted file mode 100644
index c3911c8a8e7..00000000000
--- a/src/main/java/bridge/InputView.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package bridge;
-
-/**
- * μ¬μ©μλ‘λΆν° μ
λ ₯μ λ°λ μν μ νλ€.
- */
-public class InputView {
-
- /**
- * λ€λ¦¬μ κΈΈμ΄λ₯Ό μ
λ ₯λ°λλ€.
- */
- public int readBridgeSize() {
- return 0;
- }
-
- /**
- * μ¬μ©μκ° μ΄λν μΉΈμ μ
λ ₯λ°λλ€.
- */
- public String readMoving() {
- return null;
- }
-
- /**
- * μ¬μ©μκ° κ²μμ λ€μ μλν μ§ μ’
λ£ν μ§ μ¬λΆλ₯Ό μ
λ ₯λ°λλ€.
- */
- public String readGameCommand() {
- return null;
- }
-}
diff --git a/src/main/java/bridge/OutputView.java b/src/main/java/bridge/OutputView.java
deleted file mode 100644
index 69a433a6285..00000000000
--- a/src/main/java/bridge/OutputView.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package bridge;
-
-/**
- * μ¬μ©μμκ² κ²μ μ§ν μν©κ³Ό κ²°κ³Όλ₯Ό μΆλ ₯νλ μν μ νλ€.
- */
-public class OutputView {
-
- /**
- * νμ¬κΉμ§ μ΄λν λ€λ¦¬μ μνλ₯Ό μ ν΄μ§ νμμ λ§μΆ° μΆλ ₯νλ€.
- *
- * μΆλ ₯μ μν΄ νμν λ©μλμ μΈμ(parameter)λ μμ λ‘κ² μΆκ°νκ±°λ λ³κ²½ν μ μλ€.
- */
- public void printMap() {
- }
-
- /**
- * κ²μμ μ΅μ’
κ²°κ³Όλ₯Ό μ ν΄μ§ νμμ λ§μΆ° μΆλ ₯νλ€.
- *
- * μΆλ ₯μ μν΄ νμν λ©μλμ μΈμ(parameter)λ μμ λ‘κ² μΆκ°νκ±°λ λ³κ²½ν μ μλ€.
- */
- public void printResult() {
- }
-}
diff --git a/src/main/java/bridge/controller/BridgeController.java b/src/main/java/bridge/controller/BridgeController.java
new file mode 100644
index 00000000000..7fe48ef61c1
--- /dev/null
+++ b/src/main/java/bridge/controller/BridgeController.java
@@ -0,0 +1,23 @@
+package bridge.controller;
+
+import bridge.domain.BridgeMaker;
+import bridge.util.RetryExecutor;
+import bridge.view.InputView;
+import java.util.List;
+
+public class BridgeController {
+ private final InputView inputView;
+ private final BridgeMaker bridgeMaker;
+
+ public BridgeController(InputView inputView, BridgeMaker bridgeMaker) {
+ this.inputView = inputView;
+ this.bridgeMaker = bridgeMaker;
+ }
+
+ public List prepare() {
+ int bridgeSize = RetryExecutor.retryUntilSuccess(() -> {
+ return inputView.readBridgeSize();
+ });
+ return bridgeMaker.makeBridge(bridgeSize);
+ }
+}
diff --git a/src/main/java/bridge/controller/BridgeGame.java b/src/main/java/bridge/controller/BridgeGame.java
new file mode 100644
index 00000000000..656d496bd70
--- /dev/null
+++ b/src/main/java/bridge/controller/BridgeGame.java
@@ -0,0 +1,31 @@
+package bridge.controller;
+
+import bridge.controller.dto.MoveResult;
+import bridge.view.OutputView;
+import java.util.List;
+
+/**
+ * λ€λ¦¬ 건λκΈ° κ²μμ κ΄λ¦¬νλ ν΄λμ€
+ */
+public class BridgeGame {
+ private final OutputView outputView;
+ private final BridgeController bridgeController;
+ private final MoveController moveController;
+
+ public BridgeGame(
+ OutputView outputView,
+ BridgeController bridgeController,
+ MoveController moveController
+ ) {
+ this.outputView = outputView;
+ this.bridgeController = bridgeController;
+ this.moveController = moveController;
+ this.outputView.printStartMessage();
+ }
+
+ public void run() {
+ List bridges = bridgeController.prepare();
+ MoveResult moveResult = moveController.retry(bridges);
+ outputView.printResult(moveResult);
+ }
+}
diff --git a/src/main/java/bridge/controller/MoveController.java b/src/main/java/bridge/controller/MoveController.java
new file mode 100644
index 00000000000..270caa2c9df
--- /dev/null
+++ b/src/main/java/bridge/controller/MoveController.java
@@ -0,0 +1,80 @@
+package bridge.controller;
+
+import bridge.controller.dto.Move;
+import bridge.controller.dto.MoveResult;
+import bridge.domain.constants.Result;
+import bridge.util.RetryExecutor;
+import bridge.view.InputView;
+import bridge.view.OutputView;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class MoveController {
+ private final InputView inputView;
+ private final OutputView outputView;
+
+ public MoveController(InputView inputView, OutputView outputView) {
+ this.inputView = inputView;
+ this.outputView = outputView;
+ }
+
+ /**
+ * μ¬μ©μκ° κ²μμ λ€μ μλν λ μ¬μ©νλ λ©μλ
+ */
+ public MoveResult retry(List bridges) {
+ int count = 1;
+ while (true) {
+ List moves = move(bridges);
+ Optional moveResult = createMoveResult(moves, count);
+ if (moveResult.isPresent()) {
+ return moveResult.get();
+ }
+ count++;
+ }
+ }
+
+ private Optional createMoveResult(List moves, int count) {
+ if (isSucceed(moves)) { // λκΉμ§ μ΄λμ μ±κ³΅ν κ²½μ°
+ return Optional.of(new MoveResult(moves, Result.SUCCESS, count));
+ }
+ String retry = RetryExecutor.retryUntilSuccess(inputView::readGameCommand);
+ if ("Q".equals(retry)) { // μ’
λ£
+ return Optional.of(new MoveResult(moves, Result.FAIL, count));
+ }
+ return Optional.empty(); // μ¬μμ
+ }
+
+ private boolean isSucceed(List result) {
+ int size = result.size();
+ return result.get(size - 1).success().equals("O");
+ }
+
+ /**
+ * μ¬μ©μκ° μΉΈμ μ΄λν λ μ¬μ©νλ λ©μλ
+ */
+ public List move(List bridges) {
+ List moves = new ArrayList<>();
+ for (String bridge : bridges) {
+ Move move = createSingleMove(bridge);
+ moves.add(move);
+ outputView.printMap(moves);
+ if (move.success().equals("X")) {
+ return moves;
+ }
+ }
+ return moves;
+ }
+
+ private Move createSingleMove(String bridge) {
+ String direction = RetryExecutor.retryUntilSuccess(inputView::readMoving);
+ if (cannotMove(bridge, direction)) {
+ return new Move(direction, "X");
+ }
+ return new Move(direction, "O");
+ }
+
+ private boolean cannotMove(String bridge, String direction) {
+ return !bridge.equals(direction);
+ }
+}
diff --git a/src/main/java/bridge/controller/dto/Move.java b/src/main/java/bridge/controller/dto/Move.java
new file mode 100644
index 00000000000..e01b24844a4
--- /dev/null
+++ b/src/main/java/bridge/controller/dto/Move.java
@@ -0,0 +1,19 @@
+package bridge.controller.dto;
+
+public class Move {
+ private String direction; // U, D
+ private String success; // O, X
+
+ public Move(String direction, String success) {
+ this.direction = direction;
+ this.success = success;
+ }
+
+ public String direction() {
+ return direction;
+ }
+
+ public String success() {
+ return success;
+ }
+}
diff --git a/src/main/java/bridge/controller/dto/MoveResult.java b/src/main/java/bridge/controller/dto/MoveResult.java
new file mode 100644
index 00000000000..33a6e4f7172
--- /dev/null
+++ b/src/main/java/bridge/controller/dto/MoveResult.java
@@ -0,0 +1,28 @@
+package bridge.controller.dto;
+
+import bridge.domain.constants.Result;
+import java.util.List;
+
+public class MoveResult {
+ private List moves;
+ private Result success;
+ private int count;
+
+ public MoveResult(List moves, Result success, int count) {
+ this.moves = moves;
+ this.success = success;
+ this.count = count;
+ }
+
+ public List singleMoves() {
+ return moves;
+ }
+
+ public Result success() {
+ return success;
+ }
+
+ public int count() {
+ return count;
+ }
+}
diff --git a/src/main/java/bridge/BridgeMaker.java b/src/main/java/bridge/domain/BridgeMaker.java
similarity index 56%
rename from src/main/java/bridge/BridgeMaker.java
rename to src/main/java/bridge/domain/BridgeMaker.java
index 27e9f2cfa7f..b73255b111f 100644
--- a/src/main/java/bridge/BridgeMaker.java
+++ b/src/main/java/bridge/domain/BridgeMaker.java
@@ -1,5 +1,7 @@
-package bridge;
+package bridge.domain;
+import bridge.util.BridgeNumberGenerator;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -18,6 +20,16 @@ public BridgeMaker(BridgeNumberGenerator bridgeNumberGenerator) {
* @return μ
λ ₯λ°μ κΈΈμ΄μ ν΄λΉνλ λ€λ¦¬ λͺ¨μ. μ μΉΈμ΄λ©΄ "U", μλ μΉΈμ΄λ©΄ "D"λ‘ ννν΄μΌ νλ€.
*/
public List makeBridge(int size) {
- return null;
+ List result = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ int number = bridgeNumberGenerator.generate();
+ if (number == 1) { // μ
+ result.add("U");
+ }
+ if (number == 0) { // μλ
+ result.add("D");
+ }
+ }
+ return result;
}
}
diff --git a/src/main/java/bridge/domain/constants/Result.java b/src/main/java/bridge/domain/constants/Result.java
new file mode 100644
index 00000000000..9cc21ce4abe
--- /dev/null
+++ b/src/main/java/bridge/domain/constants/Result.java
@@ -0,0 +1,16 @@
+package bridge.domain.constants;
+
+public enum Result {
+ SUCCESS("μ±κ³΅"),
+ FAIL("μ€ν¨");
+
+ private final String name;
+
+ Result(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/src/main/java/bridge/global/exception/CustomException.java b/src/main/java/bridge/global/exception/CustomException.java
new file mode 100644
index 00000000000..759725c904b
--- /dev/null
+++ b/src/main/java/bridge/global/exception/CustomException.java
@@ -0,0 +1,13 @@
+package bridge.global.exception;
+
+public class CustomException extends IllegalArgumentException {
+ private static final String PREFIX = "[ERROR] ";
+
+ private CustomException(ErrorMessage errorMessage) {
+ super(PREFIX + errorMessage.getMessage());
+ }
+
+ public static CustomException from(ErrorMessage errorMessage) {
+ return new CustomException(errorMessage);
+ }
+}
diff --git a/src/main/java/bridge/global/exception/ErrorMessage.java b/src/main/java/bridge/global/exception/ErrorMessage.java
new file mode 100644
index 00000000000..9bb9d7840b0
--- /dev/null
+++ b/src/main/java/bridge/global/exception/ErrorMessage.java
@@ -0,0 +1,19 @@
+package bridge.global.exception;
+
+public enum ErrorMessage {
+ BLANK_INPUT_ERROR("λΉ λ¬Έμμ΄μ΄ μ
λ ₯λμμ΅λλ€."),
+ NOT_NUMBER_ERROR("μ¬λ°λ₯Έ μ«μλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ."),
+ BRIDGE_SIZE_RANGE_ERROR("λ€λ¦¬ κΈΈμ΄λ 3λΆν° 20 μ¬μ΄μ μ«μμ¬μΌ ν©λλ€."),
+ INVALID_MOVING_MESSAGE("μ΄λν μΉΈμ μλͺ» μ
λ ₯νμμ΅λλ€."),
+ INVALID_GAME_COMMAND("μ¬μλ νΉμ μ’
λ£ μ¬λΆλ₯Ό μλͺ» μ
λ ₯νμμ΅λλ€.");
+
+ private final String message;
+
+ ErrorMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+}
diff --git a/src/main/java/bridge/global/validator/Validator.java b/src/main/java/bridge/global/validator/Validator.java
new file mode 100644
index 00000000000..d6a5fb12522
--- /dev/null
+++ b/src/main/java/bridge/global/validator/Validator.java
@@ -0,0 +1,32 @@
+package bridge.global.validator;
+
+import bridge.global.exception.CustomException;
+import bridge.global.exception.ErrorMessage;
+
+public final class Validator {
+ public static int validateNumber(String message, ErrorMessage errorMessage) {
+ if (isNotNumber(message)) {
+ throw CustomException.from(errorMessage);
+ }
+ return Integer.parseInt(message);
+ }
+
+ private static boolean isNotNumber(String str) {
+ return !str.matches("\\d+");
+ }
+
+ public static void validateRange(
+ int number,
+ int start,
+ int end,
+ ErrorMessage errorMessage
+ ) {
+ if (isInvalidRange(number, start, end)) {
+ throw CustomException.from(errorMessage);
+ }
+ }
+
+ private static boolean isInvalidRange(int number, int start, int end) {
+ return number < start || number > end;
+ }
+}
diff --git a/src/main/java/bridge/BridgeNumberGenerator.java b/src/main/java/bridge/util/BridgeNumberGenerator.java
similarity index 80%
rename from src/main/java/bridge/BridgeNumberGenerator.java
rename to src/main/java/bridge/util/BridgeNumberGenerator.java
index 56187b71d2d..325bff3c4be 100644
--- a/src/main/java/bridge/BridgeNumberGenerator.java
+++ b/src/main/java/bridge/util/BridgeNumberGenerator.java
@@ -1,4 +1,4 @@
-package bridge;
+package bridge.util;
@FunctionalInterface
public interface BridgeNumberGenerator {
diff --git a/src/main/java/bridge/BridgeRandomNumberGenerator.java b/src/main/java/bridge/util/BridgeRandomNumberGenerator.java
similarity index 94%
rename from src/main/java/bridge/BridgeRandomNumberGenerator.java
rename to src/main/java/bridge/util/BridgeRandomNumberGenerator.java
index 4c9cb53e03a..917f6b4eb4f 100644
--- a/src/main/java/bridge/BridgeRandomNumberGenerator.java
+++ b/src/main/java/bridge/util/BridgeRandomNumberGenerator.java
@@ -1,4 +1,4 @@
-package bridge;
+package bridge.util;
import camp.nextstep.edu.missionutils.Randoms;
diff --git a/src/main/java/bridge/util/RetryExecutor.java b/src/main/java/bridge/util/RetryExecutor.java
new file mode 100644
index 00000000000..58a227d8a37
--- /dev/null
+++ b/src/main/java/bridge/util/RetryExecutor.java
@@ -0,0 +1,16 @@
+package bridge.util;
+
+import bridge.view.console.ConsoleWriter;
+import java.util.function.Supplier;
+
+public final class RetryExecutor {
+ public static T retryUntilSuccess(Supplier supplier) {
+ while (true) {
+ try {
+ return supplier.get();
+ } catch (IllegalArgumentException e) {
+ ConsoleWriter.printlnMessage(e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/main/java/bridge/view/InputView.java b/src/main/java/bridge/view/InputView.java
new file mode 100644
index 00000000000..5bf8a78309b
--- /dev/null
+++ b/src/main/java/bridge/view/InputView.java
@@ -0,0 +1,70 @@
+package bridge.view;
+
+import bridge.global.exception.CustomException;
+import bridge.global.exception.ErrorMessage;
+import bridge.global.validator.Validator;
+import bridge.view.console.ConsoleReader;
+import bridge.view.console.ConsoleWriter;
+
+/**
+ * μ¬μ©μλ‘λΆν° μ
λ ₯μ λ°λ μν μ νλ€.
+ */
+public class InputView {
+
+ /**
+ * λ€λ¦¬μ κΈΈμ΄λ₯Ό μ
λ ₯λ°λλ€.
+ */
+ public int readBridgeSize() {
+ ConsoleWriter.println();
+ ConsoleWriter.printlnMessage("λ€λ¦¬μ κΈΈμ΄λ₯Ό μ
λ ₯ν΄μ£ΌμΈμ.");
+ return validateBridgeSize(ConsoleReader.enterMessage());
+ }
+
+ private int validateBridgeSize(String message) {
+ int size = Validator.validateNumber(
+ message,
+ ErrorMessage.NOT_NUMBER_ERROR
+ );
+ Validator.validateRange(size,
+ 3,
+ 20,
+ ErrorMessage.BRIDGE_SIZE_RANGE_ERROR
+ );
+ return size;
+ }
+
+ /**
+ * μ¬μ©μκ° μ΄λν μΉΈμ μ
λ ₯λ°λλ€.
+ */
+ public String readMoving() {
+ ConsoleWriter.printlnMessage("μ΄λν μΉΈμ μ νν΄μ£ΌμΈμ. (μ: U, μλ: D)");
+ return validateMoving(ConsoleReader.enterMessage());
+ }
+
+ private String validateMoving(String message) {
+ if (isNotMatch(message, "U") && isNotMatch(message, "D")) {
+ throw CustomException.from(ErrorMessage.INVALID_MOVING_MESSAGE);
+ }
+ return message;
+ }
+
+ private boolean isNotMatch(String actual, String expected) {
+ return !actual.equals(expected);
+ }
+
+ /**
+ * μ¬μ©μκ° κ²μμ λ€μ μλν μ§ μ’
λ£ν μ§ μ¬λΆλ₯Ό μ
λ ₯λ°λλ€.
+ */
+ public String readGameCommand() {
+ ConsoleWriter.println();
+ ConsoleWriter.printlnMessage("κ²μμ λ€μ μλν μ§ μ¬λΆλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ. (μ¬μλ: R, μ’
λ£: Q)");
+ return validateGameCommand(ConsoleReader.enterMessage());
+ }
+
+ private String validateGameCommand(String message) {
+ if (isNotMatch(message, "R") && isNotMatch(message, "Q")) {
+ throw CustomException.from(ErrorMessage.INVALID_GAME_COMMAND);
+ }
+ return message;
+ }
+}
diff --git a/src/main/java/bridge/view/OutputView.java b/src/main/java/bridge/view/OutputView.java
new file mode 100644
index 00000000000..71b4d61b7ec
--- /dev/null
+++ b/src/main/java/bridge/view/OutputView.java
@@ -0,0 +1,65 @@
+package bridge.view;
+
+import bridge.controller.dto.Move;
+import bridge.controller.dto.MoveResult;
+import bridge.view.console.ConsoleWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * μ¬μ©μμκ² κ²μ μ§ν μν©κ³Ό κ²°κ³Όλ₯Ό μΆλ ₯νλ μν μ νλ€.
+ */
+public class OutputView {
+
+ private static final String NONE = " ";
+ private static final String SINGLE_MAP_FORMAT = " | %s";
+ private static final String GAME_RESULT_MESSAGE = "κ²μ μ±κ³΅ μ¬λΆ: %s";
+ private static final String TRY_COUNT_MESSAGE = "μ΄ μλν νμ: %d";
+
+ public void printStartMessage() {
+ ConsoleWriter.printlnMessage("λ€λ¦¬ 건λκΈ° κ²μμ μμν©λλ€.");
+ }
+
+ /**
+ * νμ¬κΉμ§ μ΄λν λ€λ¦¬μ μνλ₯Ό μ ν΄μ§ νμμ λ§μΆ° μΆλ ₯νλ€.
+ */
+ public void printMap(List moves) {
+ List up = new ArrayList<>();
+ List down = new ArrayList<>();
+ for (Move move : moves) {
+ if (move.direction().equals("U")) {
+ up.add(move.success());
+ down.add(NONE);
+ }
+ if (move.direction().equals("D")) {
+ up.add(NONE);
+ down.add(move.success());
+ }
+ }
+ ConsoleWriter.printlnMessage(generateSingleMapRow(up));
+ ConsoleWriter.printlnMessage(generateSingleMapRow(down));
+ ConsoleWriter.println();
+ }
+
+ public String generateSingleMapRow(List map) {
+ int size = map.size();
+ if (size == 1) {
+ return "[ " + map.get(0) + " ]";
+ }
+ String head = "[ " + map.get(0);
+ for (int i = 1; i < size; i++) {
+ head += String.format(SINGLE_MAP_FORMAT, map.get(i));
+ }
+ return head + " ]";
+ }
+
+ /**
+ * κ²μμ μ΅μ’
κ²°κ³Όλ₯Ό μ ν΄μ§ νμμ λ§μΆ° μΆλ ₯νλ€.
+ */
+ public void printResult(MoveResult result) {
+ ConsoleWriter.printlnMessage("μ΅μ’
κ²μ κ²°κ³Ό");
+ printMap(result.singleMoves());
+ ConsoleWriter.printlnFormat(GAME_RESULT_MESSAGE, result.success().getName());
+ ConsoleWriter.printlnFormat(TRY_COUNT_MESSAGE, result.count());
+ }
+}
diff --git a/src/main/java/bridge/view/console/ConsoleReader.java b/src/main/java/bridge/view/console/ConsoleReader.java
new file mode 100644
index 00000000000..78e0c8f72a6
--- /dev/null
+++ b/src/main/java/bridge/view/console/ConsoleReader.java
@@ -0,0 +1,24 @@
+package bridge.view.console;
+
+import bridge.global.exception.CustomException;
+import bridge.global.exception.ErrorMessage;
+import camp.nextstep.edu.missionutils.Console;
+
+public final class ConsoleReader {
+ public static String enterMessage() {
+ return Validator.validate(Console.readLine());
+ }
+
+ private static class Validator {
+ public static String validate(String message) {
+ validateBlankInput(message);
+ return message;
+ }
+
+ private static void validateBlankInput(String message) {
+ if (message.isBlank()) {
+ throw CustomException.from(ErrorMessage.BLANK_INPUT_ERROR);
+ }
+ }
+ }
+}
diff --git a/src/main/java/bridge/view/console/ConsoleWriter.java b/src/main/java/bridge/view/console/ConsoleWriter.java
new file mode 100644
index 00000000000..7ab7f8d6580
--- /dev/null
+++ b/src/main/java/bridge/view/console/ConsoleWriter.java
@@ -0,0 +1,15 @@
+package bridge.view.console;
+
+public final class ConsoleWriter {
+ public static void println() {
+ System.out.println();
+ }
+
+ public static void printlnMessage(String message) {
+ System.out.println(message);
+ }
+
+ public static void printlnFormat(String message, Object... args) {
+ printlnMessage(String.format(message, args));
+ }
+}
diff --git a/src/test/java/bridge/ApplicationTest.java b/src/test/java/bridge/ApplicationTest.java
index 1a163ec0a2a..6ce58c196c8 100644
--- a/src/test/java/bridge/ApplicationTest.java
+++ b/src/test/java/bridge/ApplicationTest.java
@@ -5,6 +5,8 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.util.Lists.newArrayList;
+import bridge.domain.BridgeMaker;
+import bridge.util.BridgeNumberGenerator;
import camp.nextstep.edu.missionutils.test.NsTest;
import java.util.List;
import org.junit.jupiter.api.Test;
@@ -26,11 +28,11 @@ class ApplicationTest extends NsTest {
assertRandomNumberInRangeTest(() -> {
run("3", "U", "D", "U");
assertThat(output()).contains(
- "μ΅μ’
κ²μ κ²°κ³Ό",
- "[ O | | O ]",
- "[ | O | ]",
- "κ²μ μ±κ³΅ μ¬λΆ: μ±κ³΅",
- "μ΄ μλν νμ: 1"
+ "μ΅μ’
κ²μ κ²°κ³Ό",
+ "[ O | | O ]",
+ "[ | O | ]",
+ "κ²μ μ±κ³΅ μ¬λΆ: μ±κ³΅",
+ "μ΄ μλν νμ: 1"
);
int upSideIndex = output().indexOf("[ O | | O ]");
diff --git a/src/test/java/bridge/domain/BridgeMakerTest.java b/src/test/java/bridge/domain/BridgeMakerTest.java
new file mode 100644
index 00000000000..ab0ad2db8ff
--- /dev/null
+++ b/src/test/java/bridge/domain/BridgeMakerTest.java
@@ -0,0 +1,46 @@
+package bridge.domain;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+import bridge.util.BridgeNumberGenerator;
+import java.util.List;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+class BridgeMakerTest {
+
+ @Mock
+ private BridgeNumberGenerator bridgeNumberGenerator;
+
+ @InjectMocks
+ private BridgeMaker bridgeMaker;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void makeBridgeTestWithUp() {
+ when(bridgeNumberGenerator.generate()).thenReturn(1);
+
+ List result = bridgeMaker.makeBridge(5);
+
+ assertEquals(5, result.size());
+ assertEquals("U", result.get(0));
+ }
+
+ @Test
+ void makeBridgeTestWithDown() {
+ when(bridgeNumberGenerator.generate()).thenReturn(0);
+
+ List result = bridgeMaker.makeBridge(5);
+
+ assertEquals(5, result.size());
+ assertEquals("D", result.get(0));
+ }
+}