From 1ef3e59b56e726269785ce399985e11c20feb57c Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:05:59 +0900 Subject: [PATCH 01/16] =?UTF-8?q?docs:=20README=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=EC=A1=B0=20=EB=B0=8F=20=EC=BB=A4=EB=A7=A8?= =?UTF-8?q?=EB=93=9C=20=ED=9D=90=EB=A6=84=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d7e8aee..2f4e0707 100644 --- a/README.md +++ b/README.md @@ -1 +1,105 @@ -# java-baseball-precourse \ No newline at end of file +# 숫자 야구 게임 + +## 📌 프로젝트 소개 +1~9 사이의 서로 다른 숫자 3개로 구성된 정답을 컴퓨터가 생성하고, 사용자는 숫자를 입력하여 스트라이크/볼 힌트를 통해 정답을 맞히는 콘솔 게임입니다. +정답(3 스트라이크)을 맞히면 게임이 종료되며, 사용자는 게임을 재시작(1)하거나 완전히 종료(2)할 수 있습니다. + +--- + +## ✅ 기능 목록 + +### 게임 흐름 +- [ ] 게임 시작 시 컴퓨터가 1~9 범위의 서로 다른 숫자 3개를 생성한다. +- [ ] 사용자에게 숫자 입력을 요청하고 입력을 받는다. +- [ ] 입력한 숫자에 대한 스트라이크/볼/낫싱 힌트를 계산하여 출력한다. +- [ ] 3스트라이크가 되면 게임 종료 메시지를 출력한다. +- [ ] 게임 종료 후 `1(재시작)` / `2(종료)` 입력을 받아 처리한다. + - [ ] 재시작 시 정답 숫자를 새로 생성하여 게임을 다시 진행한다. + - [ ] 종료를 선택하면 프로그램을 종료한다. + +### 입력 검증 / 예외 처리 +- [ ] 사용자 입력이 잘못된 경우 `[ERROR]`로 시작하는 에러 메시지를 출력하고 게임을 계속 진행한다. +- [ ] 숫자 입력 검증 + - [ ] 3자리인지 검증한다. + - [ ] 각 자리가 1~9 범위인지 검증한다. (도메인: `BaseballNumber`) + - [ ] 3자리 숫자가 서로 다른 수인지 검증한다. (도메인: `BaseballNumbers`) +- [ ] 재시작/종료 입력 검증 + - [ ] 1 또는 2만 허용한다. + +--- + +## 🧱 도메인 모델 + +### `BaseballNumber` +- 한 자리 숫자를 의미하는 값 객체(Value Object) +- 유효 범위: 1~9 +- 생성 시 검증을 수행하여 항상 유효한 상태만 유지 + +### `BaseballNumbers` (일급 컬렉션) +- `BaseballNumber` 3개를 보유하는 일급 컬렉션 +- 생성 시 다음 규칙을 검증하여 항상 유효한 상태만 유지 + - 숫자 개수는 3개 + - 서로 다른 숫자(중복 없음) + +### `Hint` +- 스트라이크/볼 결과를 표현하는 값 객체 + +### `BaseballGame` +- 정답 보유 및 입력과의 비교 결과(`Hint`) 생성 + +--- + +## 🧩 패키지 구조(예시) + +- `game.baseball.domain` + - `BaseballNumber` : 한 자리 숫자(1~9) + - `BaseballNumbers` : 3자리 숫자 일급 컬렉션(개수/중복 검증) + - `Hint` : 스트라이크/볼 결과 값 객체 + - `BaseballGame` : 정답 보유 및 힌트 계산 + +- `game.baseball.application` + - `BaseballGameService` : 게임 시작 및 추측 처리(유즈케이스) + - `GuessCommandParser` : 숫자 입력 커맨드 파싱 + - `RestartCommandParser` : 재시작/종료 커맨드 파싱 + +- `game.baseball.application.port` + - `in` : `BaseballGameUseCase`, `command(GuessCommand, RestartCommand)` + - `out` : `GameInputPort`, `GameOutputPort`, `NumberGeneratorPort` + +- `game.baseball.adapter` + - `in` : `BaseballGameInputView`, `BaseballGameOutputView`, `BaseballGameController` + - `out` : `RandomNumberGenerator`, `BaseballPrinter` + +- `game` + - `Application` : 프로그램 시작점(의존성 조립) + - `GameRunner` : 실행 트리거 + +> 핵심 로직(도메인/애플리케이션)과 UI(System.in/out)를 분리하여, 도메인 로직을 단위 테스트 대상으로 삼습니다. + +--- + +## 🧪 테스트 전략 +- 도메인 로직에 대해 단위 테스트를 작성합니다. +- UI 로직(System.in/out, Scanner 등)은 테스트 범위에서 제외합니다. +- JUnit5 + AssertJ를 사용합니다. + +--- + +## ⚙️ 프로그래밍 요구사항 준수 +- indent depth는 2까지만 허용합니다. +- `else`, `switch/case`를 사용하지 않습니다. +- Java Stream API를 사용하지 않습니다. (람다는 가능) +- 메서드는 15라인을 넘지 않도록 분리합니다. + +--- + +## ▶️ 실행 방법(예시) +- IDE에서 `Application.main()` 실행 +- 또는 Gradle 기반 프로젝트라면: + - `./gradlew test` + - `./gradlew run` + +--- + +## ✍️ 커밋/진행 방식 +- 기능 구현 전 README에 기능 목록을 작성하고, 기능 단위로 커밋합니다. From be3f24278250954cef65ffd537fb25a144ad76cd Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:08:32 +0900 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=EC=88=AB=EC=9E=90=EC=95=BC?= =?UTF-8?q?=EA=B5=AC=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=ED=9E=8C=ED=8A=B8=20=EA=B3=84=EC=82=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../game/baseball/domain/BaseballGame.java | 15 ++++ .../game/baseball/domain/BaseballNumber.java | 39 +++++++++ .../game/baseball/domain/BaseballNumbers.java | 83 +++++++++++++++++++ src/main/java/game/baseball/domain/Hint.java | 67 +++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 src/main/java/game/baseball/domain/BaseballGame.java create mode 100644 src/main/java/game/baseball/domain/BaseballNumber.java create mode 100644 src/main/java/game/baseball/domain/BaseballNumbers.java create mode 100644 src/main/java/game/baseball/domain/Hint.java diff --git a/src/main/java/game/baseball/domain/BaseballGame.java b/src/main/java/game/baseball/domain/BaseballGame.java new file mode 100644 index 00000000..770ebeec --- /dev/null +++ b/src/main/java/game/baseball/domain/BaseballGame.java @@ -0,0 +1,15 @@ +package game.baseball.domain; + +public class BaseballGame { + private final BaseballNumbers answer; + + public BaseballGame(BaseballNumbers answer) { + this.answer = answer; + } + + public Hint guess(BaseballNumbers guess) { + int strike = answer.countStrike(guess); + int ball = answer.countBall(guess); + return Hint.of(strike, ball); + } +} diff --git a/src/main/java/game/baseball/domain/BaseballNumber.java b/src/main/java/game/baseball/domain/BaseballNumber.java new file mode 100644 index 00000000..3ca4e897 --- /dev/null +++ b/src/main/java/game/baseball/domain/BaseballNumber.java @@ -0,0 +1,39 @@ +package game.baseball.domain; + +public class BaseballNumber { + private final Integer number; + + private BaseballNumber(Integer number) { + this.number = number; + } + + public static BaseballNumber of(Integer number) { + validate(number); + return new BaseballNumber(number); + } + + private static void validate(final Integer number) { + if(number < 1 || number > 9) { + throw new IllegalArgumentException("야구 숫자의 범위는 1에서 9까지의 자연수 입니다."); + } + } + + public int value() { + return number; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof BaseballNumber val)) { + return false; + } + return number.equals(val.number); + } + + @Override + public int hashCode() { + return number.hashCode(); + } + +} diff --git a/src/main/java/game/baseball/domain/BaseballNumbers.java b/src/main/java/game/baseball/domain/BaseballNumbers.java new file mode 100644 index 00000000..59c3d7dc --- /dev/null +++ b/src/main/java/game/baseball/domain/BaseballNumbers.java @@ -0,0 +1,83 @@ +package game.baseball.domain; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class BaseballNumbers { + private final List numbers; + + private BaseballNumbers(List numbers) { + this.numbers = List.copyOf(numbers); + } + + public static BaseballNumbers from(final List numbers) { + validate(numbers); + return new BaseballNumbers(createBaseballNumbers(numbers)); + } + + private static void validate(final List numbers) { + validateNotNull(numbers); + validateSize(numbers); + validateDistinct(numbers); + } + + private static void validateNotNull(List numbers) { + if (numbers == null) { + throw new IllegalArgumentException("숫자 목록이 null 입니다."); + } + } + + private static void validateSize(final List numbers) { + if (numbers.size() != 3) { + throw new IllegalArgumentException("숫자야구의 숫자 개수는 3개입니다."); + } + } + + private static void validateDistinct(final List numbers) { + Set unique = new HashSet<>(); + for (Integer number : numbers) { + if (unique.contains(number)) { + throw new IllegalArgumentException("숫자는 서로 중복될 수 없습니다."); + } + unique.add(number); + } + } + + private static List createBaseballNumbers(List numbers) { + List baseballNumbers = new ArrayList<>(); + for (Integer number : numbers) { + baseballNumbers.add(BaseballNumber.of(number)); + } + return baseballNumbers; + } + + public int countStrike(BaseballNumbers guess) { + int strike = 0; + for (int i = 0; i < numbers.size(); i++) { + if (numbers.get(i).equals(guess.numbers.get(i))) { + strike++; + } + } + return strike; + } + + public int countBall(BaseballNumbers guess) { + int ball = 0; + for (int i = 0; i < numbers.size(); i++) { + BaseballNumber candidate = guess.numbers.get(i); + if (isStrikePosition(candidate, i)) { + continue; + } + if (numbers.contains(candidate)) { + ball++; + } + } + return ball; + } + + private boolean isStrikePosition(BaseballNumber candidate, int index) { + return numbers.get(index).equals(candidate); + } +} diff --git a/src/main/java/game/baseball/domain/Hint.java b/src/main/java/game/baseball/domain/Hint.java new file mode 100644 index 00000000..005346e6 --- /dev/null +++ b/src/main/java/game/baseball/domain/Hint.java @@ -0,0 +1,67 @@ +package game.baseball.domain; + +public class Hint { + private final int strike; + private final int ball; + + private Hint(int strike, int ball) { + this.strike = strike; + this.ball = ball; + } + + public static Hint of(int strike, int ball) { + validate(strike, ball); + return new Hint(strike, ball); + } + + public boolean isSolved() { + return strike == 3; + } + + public String message() { + if (isNothing()) { + return "낫싱"; + } + return buildMessage(); + } + + private boolean isNothing() { + return strike == 0 && ball == 0; + } + + private String buildMessage() { + StringBuilder sb = new StringBuilder(); + appendStrike(sb); + appendBall(sb); + return sb.toString(); + } + + private void appendStrike(StringBuilder sb) { + if (strike == 0) { + return; + } + sb.append(strike).append("스트라이크"); + } + + private void appendBall(StringBuilder sb) { + if (ball == 0) { + return; + } + if (!sb.isEmpty()) { + sb.append(" "); + } + sb.append(ball).append("볼"); + } + + private static void validate(int strike, int ball) { + if (strike < 0 || strike > 3) { + throw new IllegalArgumentException("스트라이크는 0~3 범위여야 합니다."); + } + if (ball < 0 || ball > 3) { + throw new IllegalArgumentException("볼은 0~3 범위여야 합니다."); + } + if (strike + ball > 3) { + throw new IllegalArgumentException("스트라이크와 볼의 합은 3을 초과할 수 없습니다."); + } + } +} From bdae410d2caf93a169998234e1c8ab874c07052c Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:10:02 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=ED=8F=AC=ED=8A=B8=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98=EC=99=80=20=EB=82=9C=EC=88=98=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/RandomNumberGenerator.java | 29 +++++++++++++++++++ .../port/out/NumberGeneratorPort.java | 7 +++++ 2 files changed, 36 insertions(+) create mode 100644 src/main/java/game/baseball/adapter/RandomNumberGenerator.java create mode 100644 src/main/java/game/baseball/application/port/out/NumberGeneratorPort.java diff --git a/src/main/java/game/baseball/adapter/RandomNumberGenerator.java b/src/main/java/game/baseball/adapter/RandomNumberGenerator.java new file mode 100644 index 00000000..844ea340 --- /dev/null +++ b/src/main/java/game/baseball/adapter/RandomNumberGenerator.java @@ -0,0 +1,29 @@ +package game.baseball.adapter; + +import game.baseball.application.port.out.NumberGeneratorPort; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class RandomNumberGenerator implements NumberGeneratorPort { + private final Random random = new Random(); + + @Override + public List generate() { + List numbers = new ArrayList<>(); + while (numbers.size() < 3) { + int candidate = random.nextInt(9) + 1; + addIfAbsent(numbers, candidate); + } + return numbers; + } + + private void addIfAbsent(List numbers, int candidate) { + if (numbers.contains(candidate)) { + return; + } + numbers.add(candidate); + } +} + diff --git a/src/main/java/game/baseball/application/port/out/NumberGeneratorPort.java b/src/main/java/game/baseball/application/port/out/NumberGeneratorPort.java new file mode 100644 index 00000000..f5550b2c --- /dev/null +++ b/src/main/java/game/baseball/application/port/out/NumberGeneratorPort.java @@ -0,0 +1,7 @@ +package game.baseball.application.port.out; + +import java.util.List; + +public interface NumberGeneratorPort { + List generate(); +} From a77436345e8582b0e3f9dde8cda216392c9f9e9a Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:10:23 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=EC=BD=98=EC=86=94=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5/=EC=B6=9C=EB=A0=A5=20=EC=96=B4=EB=8C=91=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/BaseballGameInputView.java | 25 +++++++++++++ .../adapter/in/BaseballGameOutputView.java | 35 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/game/baseball/adapter/in/BaseballGameInputView.java create mode 100644 src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java diff --git a/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java b/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java new file mode 100644 index 00000000..46a04b65 --- /dev/null +++ b/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java @@ -0,0 +1,25 @@ +package game.baseball.adapter.in; + +import game.baseball.application.port.out.GameInputPort; +import game.baseball.application.port.in.command.GuessCommand; +import game.baseball.application.port.in.command.RestartCommand; + +import java.util.Scanner; + +public class BaseballGameInputView implements GameInputPort { + private final Scanner scanner = new Scanner(System.in); + + @Override + public GuessCommand readGuessCommand() { + return new GuessCommand(readLine()); + } + + @Override + public RestartCommand readRestartCommand() { + return new RestartCommand(readLine()); + } + + private String readLine() { + return scanner.nextLine(); + } +} diff --git a/src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java b/src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java new file mode 100644 index 00000000..3ba9cece --- /dev/null +++ b/src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java @@ -0,0 +1,35 @@ +package game.baseball.adapter.in; + +import game.baseball.application.port.out.GameOutputPort; +import game.baseball.domain.Hint; + +public class BaseballGameOutputView implements GameOutputPort { + private static final String NUMBER_INPUT_PROMPT = "숫자를 입력해주세요 : "; + private static final String CORRECT_MESSAGE = "3개의 숫자를 모두 맞히셨습니다. 게임 종료"; + private static final String OPTION_INPUT_PROMPT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + + @Override + public void showGuessPrompt() { + System.out.print(NUMBER_INPUT_PROMPT); + } + + @Override + public void showResult(Hint hint) { + System.out.println(hint.message()); + } + + @Override + public void showGameEnd() { + System.out.println(CORRECT_MESSAGE); + } + + @Override + public void showRestartPrompt() { + System.out.println(OPTION_INPUT_PROMPT); + } + + @Override + public void showError(String message) { + System.out.println("[ERROR] " + message); + } +} From 49bd1b4cf72ab41336a7c822ce462e96d8c09adf Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:11:38 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5/=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=ED=8F=AC=ED=8A=B8=EC=99=80=20=EC=9C=A0=EC=A6=88?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4,=20=EC=BB=A4=EB=A7=A8=EB=93=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/port/in/BaseballGameUseCase.java | 10 ++++++++++ .../application/port/in/command/GuessCommand.java | 13 +++++++++++++ .../port/in/command/RestartCommand.java | 13 +++++++++++++ .../application/port/out/GameInputPort.java | 10 ++++++++++ .../application/port/out/GameOutputPort.java | 15 +++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 src/main/java/game/baseball/application/port/in/BaseballGameUseCase.java create mode 100644 src/main/java/game/baseball/application/port/in/command/GuessCommand.java create mode 100644 src/main/java/game/baseball/application/port/in/command/RestartCommand.java create mode 100644 src/main/java/game/baseball/application/port/out/GameInputPort.java create mode 100644 src/main/java/game/baseball/application/port/out/GameOutputPort.java diff --git a/src/main/java/game/baseball/application/port/in/BaseballGameUseCase.java b/src/main/java/game/baseball/application/port/in/BaseballGameUseCase.java new file mode 100644 index 00000000..481500a1 --- /dev/null +++ b/src/main/java/game/baseball/application/port/in/BaseballGameUseCase.java @@ -0,0 +1,10 @@ +package game.baseball.application.port.in; + +import game.baseball.application.port.in.command.GuessCommand; +import game.baseball.domain.Hint; + +public interface BaseballGameUseCase { + void startNewGame(); + + Hint guess(GuessCommand command); +} diff --git a/src/main/java/game/baseball/application/port/in/command/GuessCommand.java b/src/main/java/game/baseball/application/port/in/command/GuessCommand.java new file mode 100644 index 00000000..fe539c57 --- /dev/null +++ b/src/main/java/game/baseball/application/port/in/command/GuessCommand.java @@ -0,0 +1,13 @@ +package game.baseball.application.port.in.command; + +public class GuessCommand { + private final String input; + + public GuessCommand(String input) { + this.input = input; + } + + public String input() { + return input; + } +} diff --git a/src/main/java/game/baseball/application/port/in/command/RestartCommand.java b/src/main/java/game/baseball/application/port/in/command/RestartCommand.java new file mode 100644 index 00000000..f2b3af15 --- /dev/null +++ b/src/main/java/game/baseball/application/port/in/command/RestartCommand.java @@ -0,0 +1,13 @@ +package game.baseball.application.port.in.command; + +public class RestartCommand { + private final String input; + + public RestartCommand(String input) { + this.input = input; + } + + public String input() { + return input; + } +} diff --git a/src/main/java/game/baseball/application/port/out/GameInputPort.java b/src/main/java/game/baseball/application/port/out/GameInputPort.java new file mode 100644 index 00000000..4ed10114 --- /dev/null +++ b/src/main/java/game/baseball/application/port/out/GameInputPort.java @@ -0,0 +1,10 @@ +package game.baseball.application.port.out; + +import game.baseball.application.port.in.command.GuessCommand; +import game.baseball.application.port.in.command.RestartCommand; + +public interface GameInputPort { + GuessCommand readGuessCommand(); + + RestartCommand readRestartCommand(); +} diff --git a/src/main/java/game/baseball/application/port/out/GameOutputPort.java b/src/main/java/game/baseball/application/port/out/GameOutputPort.java new file mode 100644 index 00000000..745ddf4f --- /dev/null +++ b/src/main/java/game/baseball/application/port/out/GameOutputPort.java @@ -0,0 +1,15 @@ +package game.baseball.application.port.out; + +import game.baseball.domain.Hint; + +public interface GameOutputPort { + void showGuessPrompt(); + + void showResult(Hint hint); + + void showGameEnd(); + + void showRestartPrompt(); + + void showError(String message); +} From e5f1758bd6080d11a48abc339223d91a11172ea6 Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:11:57 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=20=EC=BB=A4?= =?UTF-8?q?=EB=A7=A8=EB=93=9C=20=ED=8C=8C=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/GuessCommandParser.java | 43 +++++++++++++++++++ .../application/RestartCommandParser.java | 23 ++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/game/baseball/application/GuessCommandParser.java create mode 100644 src/main/java/game/baseball/application/RestartCommandParser.java diff --git a/src/main/java/game/baseball/application/GuessCommandParser.java b/src/main/java/game/baseball/application/GuessCommandParser.java new file mode 100644 index 00000000..f83f8c22 --- /dev/null +++ b/src/main/java/game/baseball/application/GuessCommandParser.java @@ -0,0 +1,43 @@ +package game.baseball.application; + +import game.baseball.application.port.in.command.GuessCommand; +import game.baseball.domain.BaseballNumbers; + +import java.util.ArrayList; +import java.util.List; + +public class GuessCommandParser { + public BaseballNumbers parse(GuessCommand command) { + String input = normalize(command.input()); + validateLength(input); + return BaseballNumbers.from(toDigits(input)); + } + + private String normalize(String userInput) { + if (userInput == null) { + throw new IllegalArgumentException("입력값이 null 입니다."); + } + return userInput.trim(); + } + + private void validateLength(String input) { + if (input.length() != 3) { + throw new IllegalArgumentException("세 자리 숫자를 입력해야 합니다."); + } + } + + private List toDigits(String input) { + List digits = new ArrayList<>(); + for (int i = 0; i < input.length(); i++) { + digits.add(parseDigit(input.charAt(i))); + } + return digits; + } + + private int parseDigit(char ch) { + if (!Character.isDigit(ch)) { + throw new IllegalArgumentException("숫자만 입력할 수 있습니다."); + } + return ch - '0'; + } +} diff --git a/src/main/java/game/baseball/application/RestartCommandParser.java b/src/main/java/game/baseball/application/RestartCommandParser.java new file mode 100644 index 00000000..cfc2675b --- /dev/null +++ b/src/main/java/game/baseball/application/RestartCommandParser.java @@ -0,0 +1,23 @@ +package game.baseball.application; + +import game.baseball.application.port.in.command.RestartCommand; + +public class RestartCommandParser { + public boolean parse(RestartCommand command) { + String normalized = normalize(command.input()); + if ("1".equals(normalized)) { + return true; + } + if ("2".equals(normalized)) { + return false; + } + throw new IllegalArgumentException("1 또는 2를 입력해야 합니다."); + } + + private String normalize(String userInput) { + if (userInput == null) { + throw new IllegalArgumentException("입력값이 null 입니다."); + } + return userInput.trim(); + } +} From 65916d38384b420daaaecde3cbe241e04c335035 Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:12:10 +0900 Subject: [PATCH 07/16] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A6=88=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/BaseballGameService.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/game/baseball/application/BaseballGameService.java diff --git a/src/main/java/game/baseball/application/BaseballGameService.java b/src/main/java/game/baseball/application/BaseballGameService.java new file mode 100644 index 00000000..d629bb1c --- /dev/null +++ b/src/main/java/game/baseball/application/BaseballGameService.java @@ -0,0 +1,39 @@ +package game.baseball.application; + +import game.baseball.application.port.in.BaseballGameUseCase; +import game.baseball.application.port.in.command.GuessCommand; +import game.baseball.application.port.out.NumberGeneratorPort; +import game.baseball.domain.BaseballGame; +import game.baseball.domain.BaseballNumbers; +import game.baseball.domain.Hint; + +public class BaseballGameService implements BaseballGameUseCase { + private final NumberGeneratorPort numberGeneratorPort; + private final GuessCommandParser guessCommandParser; + private BaseballGame game; + + public BaseballGameService(NumberGeneratorPort numberGeneratorPort, + GuessCommandParser guessCommandParser) { + this.numberGeneratorPort = numberGeneratorPort; + this.guessCommandParser = guessCommandParser; + } + + @Override + public void startNewGame() { + BaseballNumbers answer = BaseballNumbers.from(numberGeneratorPort.generate()); + this.game = new BaseballGame(answer); + } + + @Override + public Hint guess(GuessCommand command) { + ensureGameStarted(); + BaseballNumbers guess = guessCommandParser.parse(command); + return game.guess(guess); + } + + private void ensureGameStarted() { + if (game == null) { + throw new IllegalStateException("게임이 시작되지 않았습니다."); + } + } +} From e476a7ac0d6c8ae5a0e23d12bb3f3d375f766a6f Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:12:22 +0900 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/BaseballGameController.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/main/java/game/baseball/adapter/in/BaseballGameController.java diff --git a/src/main/java/game/baseball/adapter/in/BaseballGameController.java b/src/main/java/game/baseball/adapter/in/BaseballGameController.java new file mode 100644 index 00000000..eb03b660 --- /dev/null +++ b/src/main/java/game/baseball/adapter/in/BaseballGameController.java @@ -0,0 +1,91 @@ +package game.baseball.adapter.in; + +import game.GamingConsole; +import game.baseball.application.RestartCommandParser; +import game.baseball.application.port.in.BaseballGameUseCase; +import game.baseball.application.port.in.command.GuessCommand; +import game.baseball.application.port.in.command.RestartCommand; +import game.baseball.application.port.out.GameInputPort; +import game.baseball.application.port.out.GameOutputPort; +import game.baseball.domain.Hint; + +public class BaseballGameController implements GamingConsole { + private final BaseballGameUseCase useCase; + private final GameInputPort inputPort; + private final GameOutputPort outputPort; + private final RestartCommandParser restartCommandParser; + + public BaseballGameController( + BaseballGameUseCase useCase, + GameInputPort inputPort, + GameOutputPort outputPort, + RestartCommandParser restartCommandParser + ) { + this.useCase = useCase; + this.inputPort = inputPort; + this.outputPort = outputPort; + this.restartCommandParser = restartCommandParser; + } + + @Override + public void play() { + while (true) { + useCase.startNewGame(); + playUntilSolved(); + if (!restartSelected()) { + return; + } + } + } + + private void playUntilSolved() { + while (true) { + Hint hint = readValidHint(); + outputPort.showResult(hint); + if (hint.isSolved()) { + outputPort.showGameEnd(); + return; + } + } + } + + private Hint readValidHint() { + while (true) { + Hint hint = tryGuessOnce(); + if (hint != null) { + return hint; + } + } + } + + private Hint tryGuessOnce() { + try { + outputPort.showGuessPrompt(); + GuessCommand command = inputPort.readGuessCommand(); + return useCase.guess(command); + } catch (IllegalArgumentException e) { + outputPort.showError(e.getMessage()); + return null; + } + } + + private boolean restartSelected() { + while (true) { + Boolean restart = tryReadRestartCommand(); + if (restart != null) { + return restart; + } + } + } + + private Boolean tryReadRestartCommand() { + try { + outputPort.showRestartPrompt(); + RestartCommand command = inputPort.readRestartCommand(); + return restartCommandParser.parse(command); + } catch (IllegalArgumentException e) { + outputPort.showError(e.getMessage()); + return null; + } + } +} From 9487fd4039f264496cb325339c678369a4129ebc Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:12:44 +0900 Subject: [PATCH 09/16] =?UTF-8?q?feat:=20=EA=B2=8C=EC=9E=84=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=20=EB=9F=AC=EB=84=88=EC=99=80=20=ED=94=84=EB=A6=B0?= =?UTF-8?q?=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/game/GamePrinter.java | 6 ++++++ src/main/java/game/GameRunner.java | 17 +++++++++++++++++ src/main/java/game/GamingConsole.java | 5 +++++ .../baseball/adapter/out/BaseballPrinter.java | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 src/main/java/game/GamePrinter.java create mode 100644 src/main/java/game/GameRunner.java create mode 100644 src/main/java/game/GamingConsole.java create mode 100644 src/main/java/game/baseball/adapter/out/BaseballPrinter.java diff --git a/src/main/java/game/GamePrinter.java b/src/main/java/game/GamePrinter.java new file mode 100644 index 00000000..30070c32 --- /dev/null +++ b/src/main/java/game/GamePrinter.java @@ -0,0 +1,6 @@ +package game; + +public interface GamePrinter { + void printStartingMessage(); + void printEndingMessage(); +} diff --git a/src/main/java/game/GameRunner.java b/src/main/java/game/GameRunner.java new file mode 100644 index 00000000..b7abd7da --- /dev/null +++ b/src/main/java/game/GameRunner.java @@ -0,0 +1,17 @@ +package game; + +public class GameRunner { + private final GamingConsole nowRunningGame; + private final GamePrinter nowRunningGamePrinter; + + public GameRunner(GamingConsole nowRunningGame, GamePrinter nowRunningGamePrinter) { + this.nowRunningGame = nowRunningGame; + this.nowRunningGamePrinter = nowRunningGamePrinter; + } + + public void run() { + nowRunningGamePrinter.printStartingMessage(); + nowRunningGame.play(); + nowRunningGamePrinter.printEndingMessage(); + } +} diff --git a/src/main/java/game/GamingConsole.java b/src/main/java/game/GamingConsole.java new file mode 100644 index 00000000..0573e40f --- /dev/null +++ b/src/main/java/game/GamingConsole.java @@ -0,0 +1,5 @@ +package game; + +public interface GamingConsole { + void play(); +} diff --git a/src/main/java/game/baseball/adapter/out/BaseballPrinter.java b/src/main/java/game/baseball/adapter/out/BaseballPrinter.java new file mode 100644 index 00000000..f9bd70ee --- /dev/null +++ b/src/main/java/game/baseball/adapter/out/BaseballPrinter.java @@ -0,0 +1,18 @@ +package game.baseball.adapter.out; + +import game.GamePrinter; + +public class BaseballPrinter implements GamePrinter { + private static final String START_MESSAGE = "숫자 야구 게임을 시작합니다."; + private static final String END_MESSAGE = "게임을 종료합니다."; + + @Override + public void printStartingMessage() { + System.out.println(START_MESSAGE); + } + + @Override + public void printEndingMessage() { + System.out.println(END_MESSAGE); + } +} From dc5a0205803d28bf708a8373e70257fa14ea2e34 Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:12:55 +0900 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=8B=A4=ED=96=89=20=EA=B5=AC?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/Application.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 00000000..b9e00df5 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,38 @@ +import game.GamePrinter; +import game.GameRunner; +import game.GamingConsole; +import game.baseball.adapter.RandomNumberGenerator; +import game.baseball.adapter.in.BaseballGameController; +import game.baseball.adapter.in.BaseballGameInputView; +import game.baseball.adapter.in.BaseballGameOutputView; +import game.baseball.adapter.out.BaseballPrinter; +import game.baseball.application.GuessCommandParser; +import game.baseball.application.BaseballGameService; +import game.baseball.application.RestartCommandParser; +import game.baseball.application.port.in.BaseballGameUseCase; +import game.baseball.application.port.out.GameInputPort; +import game.baseball.application.port.out.GameOutputPort; +import game.baseball.application.port.out.NumberGeneratorPort; + +public class Application { + public static void main(String[] args) { + NumberGeneratorPort numberGeneratorPort = new RandomNumberGenerator(); + GameInputPort inputPort = new BaseballGameInputView(); + GameOutputPort outputPort = new BaseballGameOutputView(); + GuessCommandParser guessCommandParser = new GuessCommandParser(); + RestartCommandParser restartCommandParser = new RestartCommandParser(); + BaseballGameUseCase useCase = new BaseballGameService(numberGeneratorPort, guessCommandParser); + GamingConsole console = new BaseballGameController( + useCase, + inputPort, + outputPort, + restartCommandParser + ); + GamePrinter printer = new BaseballPrinter(); + GameRunner gameRunner = new GameRunner( + console, + printer + ); + gameRunner.run(); + } +} From 31bad2eb5b02ec93943383cbf6769e771d1b8978 Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:19:12 +0900 Subject: [PATCH 11/16] =?UTF-8?q?test:=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../baseball/domain/BaseballGameTest.java | 38 +++++++++++++ .../baseball/domain/BaseballNumberTest.java | 28 ++++++++++ .../baseball/domain/BaseballNumbersTest.java | 45 +++++++++++++++ .../java/game/baseball/domain/HintTest.java | 55 +++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 src/test/java/game/baseball/domain/BaseballGameTest.java create mode 100644 src/test/java/game/baseball/domain/BaseballNumberTest.java create mode 100644 src/test/java/game/baseball/domain/BaseballNumbersTest.java create mode 100644 src/test/java/game/baseball/domain/HintTest.java diff --git a/src/test/java/game/baseball/domain/BaseballGameTest.java b/src/test/java/game/baseball/domain/BaseballGameTest.java new file mode 100644 index 00000000..2904ae3f --- /dev/null +++ b/src/test/java/game/baseball/domain/BaseballGameTest.java @@ -0,0 +1,38 @@ +package game.baseball.domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class BaseballGameTest { + + @Test + void guessAllStrike() { + BaseballGame game = new BaseballGame(BaseballNumbers.from(List.of(1, 2, 3))); + + Hint hint = game.guess(BaseballNumbers.from(List.of(1, 2, 3))); + + assertThat(hint.isSolved()).isTrue(); + assertThat(hint.message()).isEqualTo("3스트라이크"); + } + + @Test + void guessStrikeAndBall() { + BaseballGame game = new BaseballGame(BaseballNumbers.from(List.of(1, 2, 3))); + + Hint hint = game.guess(BaseballNumbers.from(List.of(1, 3, 2))); + + assertThat(hint.message()).isEqualTo("1스트라이크 2볼"); + } + + @Test + void guessNothing() { + BaseballGame game = new BaseballGame(BaseballNumbers.from(List.of(1, 2, 3))); + + Hint hint = game.guess(BaseballNumbers.from(List.of(4, 5, 6))); + + assertThat(hint.message()).isEqualTo("낫싱"); + } +} diff --git a/src/test/java/game/baseball/domain/BaseballNumberTest.java b/src/test/java/game/baseball/domain/BaseballNumberTest.java new file mode 100644 index 00000000..917a8cd0 --- /dev/null +++ b/src/test/java/game/baseball/domain/BaseballNumberTest.java @@ -0,0 +1,28 @@ +package game.baseball.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BaseballNumberTest { + + @Test + void createWithValidRange() { + BaseballNumber number = BaseballNumber.of(1); + + assertThat(number.value()).isEqualTo(1); + } + + @Test + void rejectNumberLessThanOne() { + assertThatThrownBy(() -> BaseballNumber.of(0)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void rejectNumberGreaterThanNine() { + assertThatThrownBy(() -> BaseballNumber.of(10)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/game/baseball/domain/BaseballNumbersTest.java b/src/test/java/game/baseball/domain/BaseballNumbersTest.java new file mode 100644 index 00000000..c28d127f --- /dev/null +++ b/src/test/java/game/baseball/domain/BaseballNumbersTest.java @@ -0,0 +1,45 @@ +package game.baseball.domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class BaseballNumbersTest { + + @Test + void createWithValidNumbers() { + BaseballNumbers numbers = BaseballNumbers.from(List.of(1, 2, 3)); + + assertThat(numbers.countStrike(BaseballNumbers.from(List.of(1, 2, 3)))).isEqualTo(3); + } + + @Test + void rejectNullList() { + assertThatThrownBy(() -> BaseballNumbers.from(null)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void rejectWrongSize() { + assertThatThrownBy(() -> BaseballNumbers.from(List.of(1, 2))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void rejectDuplicateNumbers() { + assertThatThrownBy(() -> BaseballNumbers.from(List.of(1, 1, 2))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void countStrikeAndBall() { + BaseballNumbers answer = BaseballNumbers.from(List.of(1, 2, 3)); + BaseballNumbers guess = BaseballNumbers.from(List.of(1, 3, 2)); + + assertThat(answer.countStrike(guess)).isEqualTo(1); + assertThat(answer.countBall(guess)).isEqualTo(2); + } +} diff --git a/src/test/java/game/baseball/domain/HintTest.java b/src/test/java/game/baseball/domain/HintTest.java new file mode 100644 index 00000000..155ff093 --- /dev/null +++ b/src/test/java/game/baseball/domain/HintTest.java @@ -0,0 +1,55 @@ +package game.baseball.domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class HintTest { + + @Test + void messageWhenNothing() { + Hint hint = Hint.of(0, 0); + + assertThat(hint.message()).isEqualTo("낫싱"); + } + + @Test + void messageWhenStrikeAndBall() { + Hint hint = Hint.of(1, 2); + + assertThat(hint.message()).isEqualTo("1스트라이크 2볼"); + } + + @Test + void messageWhenOnlyBall() { + Hint hint = Hint.of(0, 2); + + assertThat(hint.message()).isEqualTo("2볼"); + } + + @Test + void solvedWhenThreeStrike() { + Hint hint = Hint.of(3, 0); + + assertThat(hint.isSolved()).isTrue(); + } + + @Test + void rejectInvalidStrikeRange() { + assertThatThrownBy(() -> Hint.of(4, 0)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void rejectInvalidBallRange() { + assertThatThrownBy(() -> Hint.of(0, 4)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void rejectSumGreaterThanThree() { + assertThatThrownBy(() -> Hint.of(2, 2)) + .isInstanceOf(IllegalArgumentException.class); + } +} From 4c0e403e06e799d9c12bcc4a55226fc900fa9517 Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:22:31 +0900 Subject: [PATCH 12/16] =?UTF-8?q?docs:=20README=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2f4e0707..3a58162d 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,22 @@ ## ✅ 기능 목록 ### 게임 흐름 -- [ ] 게임 시작 시 컴퓨터가 1~9 범위의 서로 다른 숫자 3개를 생성한다. -- [ ] 사용자에게 숫자 입력을 요청하고 입력을 받는다. -- [ ] 입력한 숫자에 대한 스트라이크/볼/낫싱 힌트를 계산하여 출력한다. -- [ ] 3스트라이크가 되면 게임 종료 메시지를 출력한다. -- [ ] 게임 종료 후 `1(재시작)` / `2(종료)` 입력을 받아 처리한다. - - [ ] 재시작 시 정답 숫자를 새로 생성하여 게임을 다시 진행한다. - - [ ] 종료를 선택하면 프로그램을 종료한다. +- [x] 게임 시작 시 컴퓨터가 1~9 범위의 서로 다른 숫자 3개를 생성한다. +- [x] 사용자에게 숫자 입력을 요청하고 입력을 받는다. +- [x] 입력한 숫자에 대한 스트라이크/볼/낫싱 힌트를 계산하여 출력한다. +- [x] 3스트라이크가 되면 게임 종료 메시지를 출력한다. +- [x] 게임 종료 후 `1(재시작)` / `2(종료)` 입력을 받아 처리한다. + - [x] 재시작 시 정답 숫자를 새로 생성하여 게임을 다시 진행한다. + - [x] 종료를 선택하면 프로그램을 종료한다. ### 입력 검증 / 예외 처리 -- [ ] 사용자 입력이 잘못된 경우 `[ERROR]`로 시작하는 에러 메시지를 출력하고 게임을 계속 진행한다. -- [ ] 숫자 입력 검증 - - [ ] 3자리인지 검증한다. - - [ ] 각 자리가 1~9 범위인지 검증한다. (도메인: `BaseballNumber`) - - [ ] 3자리 숫자가 서로 다른 수인지 검증한다. (도메인: `BaseballNumbers`) -- [ ] 재시작/종료 입력 검증 - - [ ] 1 또는 2만 허용한다. +- [x] 사용자 입력이 잘못된 경우 `[ERROR]`로 시작하는 에러 메시지를 출력하고 게임을 계속 진행한다. +- [x] 숫자 입력 검증 + - [x] 3자리인지 검증한다. + - [x] 각 자리가 1~9 범위인지 검증한다. (도메인: `BaseballNumber`) + - [x] 3자리 숫자가 서로 다른 수인지 검증한다. (도메인: `BaseballNumbers`) +- [x] 재시작/종료 입력 검증 + - [x] 1 또는 2만 허용한다. --- From 7b129d1c7a196a83bbca9bda3cfcdf441a5b25e5 Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:48:17 +0900 Subject: [PATCH 13/16] =?UTF-8?q?refactor:=20=EC=BB=A4=EB=A7=A8=EB=93=9C?= =?UTF-8?q?=20=EB=8B=A8=EC=88=9C=ED=99=94=20=EB=B0=8F=20=ED=8C=8C=EC=84=9C?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 23 ++++----- .../adapter/in/BaseballGameController.java | 8 +-- .../adapter/in/BaseballGameInputView.java | 51 ++++++++++++++++++- .../application/BaseballGameService.java | 7 +-- .../application/GuessCommandParser.java | 43 ---------------- .../application/RestartCommandParser.java | 23 --------- .../port/in/command/GuessCommand.java | 12 ++--- .../port/in/command/RestartCommand.java | 11 +--- 8 files changed, 69 insertions(+), 109 deletions(-) delete mode 100644 src/main/java/game/baseball/application/GuessCommandParser.java delete mode 100644 src/main/java/game/baseball/application/RestartCommandParser.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java index b9e00df5..6f15fb29 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -6,9 +6,7 @@ import game.baseball.adapter.in.BaseballGameInputView; import game.baseball.adapter.in.BaseballGameOutputView; import game.baseball.adapter.out.BaseballPrinter; -import game.baseball.application.GuessCommandParser; import game.baseball.application.BaseballGameService; -import game.baseball.application.RestartCommandParser; import game.baseball.application.port.in.BaseballGameUseCase; import game.baseball.application.port.out.GameInputPort; import game.baseball.application.port.out.GameOutputPort; @@ -18,16 +16,7 @@ public class Application { public static void main(String[] args) { NumberGeneratorPort numberGeneratorPort = new RandomNumberGenerator(); GameInputPort inputPort = new BaseballGameInputView(); - GameOutputPort outputPort = new BaseballGameOutputView(); - GuessCommandParser guessCommandParser = new GuessCommandParser(); - RestartCommandParser restartCommandParser = new RestartCommandParser(); - BaseballGameUseCase useCase = new BaseballGameService(numberGeneratorPort, guessCommandParser); - GamingConsole console = new BaseballGameController( - useCase, - inputPort, - outputPort, - restartCommandParser - ); + GamingConsole console = getGamingConsole(numberGeneratorPort, inputPort); GamePrinter printer = new BaseballPrinter(); GameRunner gameRunner = new GameRunner( console, @@ -35,4 +24,14 @@ public static void main(String[] args) { ); gameRunner.run(); } + + private static GamingConsole getGamingConsole(NumberGeneratorPort numberGeneratorPort, GameInputPort inputPort) { + GameOutputPort outputPort = new BaseballGameOutputView(); + BaseballGameUseCase useCase = new BaseballGameService(numberGeneratorPort); + return new BaseballGameController( + useCase, + inputPort, + outputPort + ); + } } diff --git a/src/main/java/game/baseball/adapter/in/BaseballGameController.java b/src/main/java/game/baseball/adapter/in/BaseballGameController.java index eb03b660..9a7e4eea 100644 --- a/src/main/java/game/baseball/adapter/in/BaseballGameController.java +++ b/src/main/java/game/baseball/adapter/in/BaseballGameController.java @@ -1,7 +1,6 @@ package game.baseball.adapter.in; import game.GamingConsole; -import game.baseball.application.RestartCommandParser; import game.baseball.application.port.in.BaseballGameUseCase; import game.baseball.application.port.in.command.GuessCommand; import game.baseball.application.port.in.command.RestartCommand; @@ -13,18 +12,15 @@ public class BaseballGameController implements GamingConsole { private final BaseballGameUseCase useCase; private final GameInputPort inputPort; private final GameOutputPort outputPort; - private final RestartCommandParser restartCommandParser; public BaseballGameController( BaseballGameUseCase useCase, GameInputPort inputPort, - GameOutputPort outputPort, - RestartCommandParser restartCommandParser + GameOutputPort outputPort ) { this.useCase = useCase; this.inputPort = inputPort; this.outputPort = outputPort; - this.restartCommandParser = restartCommandParser; } @Override @@ -82,7 +78,7 @@ private Boolean tryReadRestartCommand() { try { outputPort.showRestartPrompt(); RestartCommand command = inputPort.readRestartCommand(); - return restartCommandParser.parse(command); + return command.restart(); } catch (IllegalArgumentException e) { outputPort.showError(e.getMessage()); return null; diff --git a/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java b/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java index 46a04b65..4f13571f 100644 --- a/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java +++ b/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java @@ -4,6 +4,8 @@ import game.baseball.application.port.in.command.GuessCommand; import game.baseball.application.port.in.command.RestartCommand; +import java.util.ArrayList; +import java.util.List; import java.util.Scanner; public class BaseballGameInputView implements GameInputPort { @@ -11,15 +13,60 @@ public class BaseballGameInputView implements GameInputPort { @Override public GuessCommand readGuessCommand() { - return new GuessCommand(readLine()); + return new GuessCommand(parseGuess(readLine())); } @Override public RestartCommand readRestartCommand() { - return new RestartCommand(readLine()); + return new RestartCommand(parseRestart(readLine())); } private String readLine() { return scanner.nextLine(); } + + private List parseGuess(String userInput) { + String input = normalize(userInput); + validateLength(input); + return toDigits(input); + } + + private String normalize(String userInput) { + if (userInput == null) { + throw new IllegalArgumentException("입력값이 null 입니다."); + } + return userInput.trim(); + } + + private void validateLength(String input) { + if (input.length() != 3) { + throw new IllegalArgumentException("세 자리 숫자를 입력해야 합니다."); + } + } + + private List toDigits(String input) { + List digits = new ArrayList<>(); + for (int i = 0; i < input.length(); i++) { + digits.add(parseDigit(input.charAt(i))); + } + return digits; + } + + private int parseDigit(char ch) { + if (!Character.isDigit(ch)) { + throw new IllegalArgumentException("숫자만 입력할 수 있습니다."); + } + return ch - '0'; + } + + private boolean parseRestart(String command) { + String normalized = normalize(command); + if ("1".equals(normalized)) { + return true; + } + if ("2".equals(normalized)) { + return false; + } + throw new IllegalArgumentException("1 또는 2를 입력해야 합니다."); + } } diff --git a/src/main/java/game/baseball/application/BaseballGameService.java b/src/main/java/game/baseball/application/BaseballGameService.java index d629bb1c..18a2fde5 100644 --- a/src/main/java/game/baseball/application/BaseballGameService.java +++ b/src/main/java/game/baseball/application/BaseballGameService.java @@ -9,13 +9,10 @@ public class BaseballGameService implements BaseballGameUseCase { private final NumberGeneratorPort numberGeneratorPort; - private final GuessCommandParser guessCommandParser; private BaseballGame game; - public BaseballGameService(NumberGeneratorPort numberGeneratorPort, - GuessCommandParser guessCommandParser) { + public BaseballGameService(NumberGeneratorPort numberGeneratorPort) { this.numberGeneratorPort = numberGeneratorPort; - this.guessCommandParser = guessCommandParser; } @Override @@ -27,7 +24,7 @@ public void startNewGame() { @Override public Hint guess(GuessCommand command) { ensureGameStarted(); - BaseballNumbers guess = guessCommandParser.parse(command); + BaseballNumbers guess = BaseballNumbers.from(command.digits()); return game.guess(guess); } diff --git a/src/main/java/game/baseball/application/GuessCommandParser.java b/src/main/java/game/baseball/application/GuessCommandParser.java deleted file mode 100644 index f83f8c22..00000000 --- a/src/main/java/game/baseball/application/GuessCommandParser.java +++ /dev/null @@ -1,43 +0,0 @@ -package game.baseball.application; - -import game.baseball.application.port.in.command.GuessCommand; -import game.baseball.domain.BaseballNumbers; - -import java.util.ArrayList; -import java.util.List; - -public class GuessCommandParser { - public BaseballNumbers parse(GuessCommand command) { - String input = normalize(command.input()); - validateLength(input); - return BaseballNumbers.from(toDigits(input)); - } - - private String normalize(String userInput) { - if (userInput == null) { - throw new IllegalArgumentException("입력값이 null 입니다."); - } - return userInput.trim(); - } - - private void validateLength(String input) { - if (input.length() != 3) { - throw new IllegalArgumentException("세 자리 숫자를 입력해야 합니다."); - } - } - - private List toDigits(String input) { - List digits = new ArrayList<>(); - for (int i = 0; i < input.length(); i++) { - digits.add(parseDigit(input.charAt(i))); - } - return digits; - } - - private int parseDigit(char ch) { - if (!Character.isDigit(ch)) { - throw new IllegalArgumentException("숫자만 입력할 수 있습니다."); - } - return ch - '0'; - } -} diff --git a/src/main/java/game/baseball/application/RestartCommandParser.java b/src/main/java/game/baseball/application/RestartCommandParser.java deleted file mode 100644 index cfc2675b..00000000 --- a/src/main/java/game/baseball/application/RestartCommandParser.java +++ /dev/null @@ -1,23 +0,0 @@ -package game.baseball.application; - -import game.baseball.application.port.in.command.RestartCommand; - -public class RestartCommandParser { - public boolean parse(RestartCommand command) { - String normalized = normalize(command.input()); - if ("1".equals(normalized)) { - return true; - } - if ("2".equals(normalized)) { - return false; - } - throw new IllegalArgumentException("1 또는 2를 입력해야 합니다."); - } - - private String normalize(String userInput) { - if (userInput == null) { - throw new IllegalArgumentException("입력값이 null 입니다."); - } - return userInput.trim(); - } -} diff --git a/src/main/java/game/baseball/application/port/in/command/GuessCommand.java b/src/main/java/game/baseball/application/port/in/command/GuessCommand.java index fe539c57..f66c6d0b 100644 --- a/src/main/java/game/baseball/application/port/in/command/GuessCommand.java +++ b/src/main/java/game/baseball/application/port/in/command/GuessCommand.java @@ -1,13 +1,9 @@ package game.baseball.application.port.in.command; -public class GuessCommand { - private final String input; +import java.util.List; - public GuessCommand(String input) { - this.input = input; - } - - public String input() { - return input; +public record GuessCommand(List digits) { + public GuessCommand { + digits = List.copyOf(digits); } } diff --git a/src/main/java/game/baseball/application/port/in/command/RestartCommand.java b/src/main/java/game/baseball/application/port/in/command/RestartCommand.java index f2b3af15..331936c3 100644 --- a/src/main/java/game/baseball/application/port/in/command/RestartCommand.java +++ b/src/main/java/game/baseball/application/port/in/command/RestartCommand.java @@ -1,13 +1,4 @@ package game.baseball.application.port.in.command; -public class RestartCommand { - private final String input; - - public RestartCommand(String input) { - this.input = input; - } - - public String input() { - return input; - } +public record RestartCommand(boolean restart) { } From bd178cafa890d755f3d59c15215ea0e5877dbd90 Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 21:55:52 +0900 Subject: [PATCH 14/16] =?UTF-8?q?docs:=20=ED=8C=8C=EC=84=9C=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=96=B8=EA=B8=89=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 3a58162d..85fb4975 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,6 @@ - `game.baseball.application` - `BaseballGameService` : 게임 시작 및 추측 처리(유즈케이스) - - `GuessCommandParser` : 숫자 입력 커맨드 파싱 - - `RestartCommandParser` : 재시작/종료 커맨드 파싱 - `game.baseball.application.port` - `in` : `BaseballGameUseCase`, `command(GuessCommand, RestartCommand)` From dab2bdffb9ec9a7a7413a3429a0c33fb2a55bb5b Mon Sep 17 00:00:00 2001 From: haebyun Date: Mon, 2 Feb 2026 22:02:36 +0900 Subject: [PATCH 15/16] =?UTF-8?q?refactor:=20=EB=A7=A4=EC=A7=81=20?= =?UTF-8?q?=EB=84=98=EB=B2=84=EB=A5=BC=20=EC=9D=98=EB=AF=B8=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=EC=88=98=EB=A1=9C=20=EC=B9=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/RandomNumberGenerator.java | 10 +++++-- .../adapter/in/BaseballGameInputView.java | 21 +++++++++---- .../adapter/in/BaseballGameOutputView.java | 10 +++++-- .../game/baseball/domain/BaseballNumber.java | 9 ++++-- .../game/baseball/domain/BaseballNumbers.java | 8 +++-- src/main/java/game/baseball/domain/Hint.java | 30 ++++++++++++------- 6 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/main/java/game/baseball/adapter/RandomNumberGenerator.java b/src/main/java/game/baseball/adapter/RandomNumberGenerator.java index 844ea340..4f59f6f9 100644 --- a/src/main/java/game/baseball/adapter/RandomNumberGenerator.java +++ b/src/main/java/game/baseball/adapter/RandomNumberGenerator.java @@ -7,13 +7,18 @@ import java.util.Random; public class RandomNumberGenerator implements NumberGeneratorPort { + private static final int NUMBER_COUNT = 3; + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 9; + private static final int RANDOM_RANGE = MAX_NUMBER - MIN_NUMBER + 1; + private final Random random = new Random(); @Override public List generate() { List numbers = new ArrayList<>(); - while (numbers.size() < 3) { - int candidate = random.nextInt(9) + 1; + while (numbers.size() < NUMBER_COUNT) { + int candidate = random.nextInt(RANDOM_RANGE) + MIN_NUMBER; addIfAbsent(numbers, candidate); } return numbers; @@ -26,4 +31,3 @@ private void addIfAbsent(List numbers, int candidate) { numbers.add(candidate); } } - diff --git a/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java b/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java index 4f13571f..8147f797 100644 --- a/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java +++ b/src/main/java/game/baseball/adapter/in/BaseballGameInputView.java @@ -9,6 +9,11 @@ import java.util.Scanner; public class BaseballGameInputView implements GameInputPort { + private static final int GUESS_LENGTH = 3; + private static final char DIGIT_ZERO = '0'; + private static final String RESTART_COMMAND = "1"; + private static final String QUIT_COMMAND = "2"; + private final Scanner scanner = new Scanner(System.in); @Override @@ -39,8 +44,10 @@ private String normalize(String userInput) { } private void validateLength(String input) { - if (input.length() != 3) { - throw new IllegalArgumentException("세 자리 숫자를 입력해야 합니다."); + if (input.length() != GUESS_LENGTH) { + throw new IllegalArgumentException( + String.format("%d자리 숫자를 입력해야 합니다.", GUESS_LENGTH) + ); } } @@ -56,17 +63,19 @@ private int parseDigit(char ch) { if (!Character.isDigit(ch)) { throw new IllegalArgumentException("숫자만 입력할 수 있습니다."); } - return ch - '0'; + return ch - DIGIT_ZERO; } private boolean parseRestart(String command) { String normalized = normalize(command); - if ("1".equals(normalized)) { + if (RESTART_COMMAND.equals(normalized)) { return true; } - if ("2".equals(normalized)) { + if (QUIT_COMMAND.equals(normalized)) { return false; } - throw new IllegalArgumentException("1 또는 2를 입력해야 합니다."); + throw new IllegalArgumentException( + String.format("%s 또는 %s를 입력해야 합니다.", RESTART_COMMAND, QUIT_COMMAND) + ); } } diff --git a/src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java b/src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java index 3ba9cece..6b2c0a48 100644 --- a/src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java +++ b/src/main/java/game/baseball/adapter/in/BaseballGameOutputView.java @@ -4,9 +4,15 @@ import game.baseball.domain.Hint; public class BaseballGameOutputView implements GameOutputPort { + private static final int ANSWER_LENGTH = 3; + private static final String RESTART_OPTION = "1"; + private static final String QUIT_OPTION = "2"; + private static final String NUMBER_INPUT_PROMPT = "숫자를 입력해주세요 : "; - private static final String CORRECT_MESSAGE = "3개의 숫자를 모두 맞히셨습니다. 게임 종료"; - private static final String OPTION_INPUT_PROMPT = "게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."; + private static final String CORRECT_MESSAGE = + String.format("%d개의 숫자를 모두 맞히셨습니다. 게임 종료", ANSWER_LENGTH); + private static final String OPTION_INPUT_PROMPT = + String.format("게임을 새로 시작하려면 %s, 종료하려면 %s를 입력하세요.", RESTART_OPTION, QUIT_OPTION); @Override public void showGuessPrompt() { diff --git a/src/main/java/game/baseball/domain/BaseballNumber.java b/src/main/java/game/baseball/domain/BaseballNumber.java index 3ca4e897..57173db7 100644 --- a/src/main/java/game/baseball/domain/BaseballNumber.java +++ b/src/main/java/game/baseball/domain/BaseballNumber.java @@ -1,6 +1,9 @@ package game.baseball.domain; public class BaseballNumber { + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 9; + private final Integer number; private BaseballNumber(Integer number) { @@ -13,8 +16,10 @@ public static BaseballNumber of(Integer number) { } private static void validate(final Integer number) { - if(number < 1 || number > 9) { - throw new IllegalArgumentException("야구 숫자의 범위는 1에서 9까지의 자연수 입니다."); + if (number < MIN_NUMBER || number > MAX_NUMBER) { + throw new IllegalArgumentException( + String.format("야구 숫자의 범위는 %d에서 %d까지의 자연수 입니다.", MIN_NUMBER, MAX_NUMBER) + ); } } diff --git a/src/main/java/game/baseball/domain/BaseballNumbers.java b/src/main/java/game/baseball/domain/BaseballNumbers.java index 59c3d7dc..513aad4c 100644 --- a/src/main/java/game/baseball/domain/BaseballNumbers.java +++ b/src/main/java/game/baseball/domain/BaseballNumbers.java @@ -6,6 +6,8 @@ import java.util.Set; public class BaseballNumbers { + private static final int NUMBERS_SIZE = 3; + private final List numbers; private BaseballNumbers(List numbers) { @@ -30,8 +32,10 @@ private static void validateNotNull(List numbers) { } private static void validateSize(final List numbers) { - if (numbers.size() != 3) { - throw new IllegalArgumentException("숫자야구의 숫자 개수는 3개입니다."); + if (numbers.size() != NUMBERS_SIZE) { + throw new IllegalArgumentException( + String.format("숫자야구의 숫자 개수는 %d개입니다.", NUMBERS_SIZE) + ); } } diff --git a/src/main/java/game/baseball/domain/Hint.java b/src/main/java/game/baseball/domain/Hint.java index 005346e6..866671d2 100644 --- a/src/main/java/game/baseball/domain/Hint.java +++ b/src/main/java/game/baseball/domain/Hint.java @@ -1,6 +1,10 @@ package game.baseball.domain; public class Hint { + private static final int MIN_COUNT = 0; + private static final int MAX_COUNT = 3; + private static final int SOLVED_STRIKE_COUNT = 3; + private final int strike; private final int ball; @@ -15,7 +19,7 @@ public static Hint of(int strike, int ball) { } public boolean isSolved() { - return strike == 3; + return strike == SOLVED_STRIKE_COUNT; } public String message() { @@ -26,7 +30,7 @@ public String message() { } private boolean isNothing() { - return strike == 0 && ball == 0; + return strike == MIN_COUNT && ball == MIN_COUNT; } private String buildMessage() { @@ -37,14 +41,14 @@ private String buildMessage() { } private void appendStrike(StringBuilder sb) { - if (strike == 0) { + if (strike == MIN_COUNT) { return; } sb.append(strike).append("스트라이크"); } private void appendBall(StringBuilder sb) { - if (ball == 0) { + if (ball == MIN_COUNT) { return; } if (!sb.isEmpty()) { @@ -54,14 +58,20 @@ private void appendBall(StringBuilder sb) { } private static void validate(int strike, int ball) { - if (strike < 0 || strike > 3) { - throw new IllegalArgumentException("스트라이크는 0~3 범위여야 합니다."); + if (strike < MIN_COUNT || strike > MAX_COUNT) { + throw new IllegalArgumentException( + String.format("스트라이크는 %d~%d 범위여야 합니다.", MIN_COUNT, MAX_COUNT) + ); } - if (ball < 0 || ball > 3) { - throw new IllegalArgumentException("볼은 0~3 범위여야 합니다."); + if (ball < MIN_COUNT || ball > MAX_COUNT) { + throw new IllegalArgumentException( + String.format("볼은 %d~%d 범위여야 합니다.", MIN_COUNT, MAX_COUNT) + ); } - if (strike + ball > 3) { - throw new IllegalArgumentException("스트라이크와 볼의 합은 3을 초과할 수 없습니다."); + if (strike + ball > MAX_COUNT) { + throw new IllegalArgumentException( + String.format("스트라이크와 볼의 합은 %d을 초과할 수 없습니다.", MAX_COUNT) + ); } } } From 25df3644bf1fa32cd2b28a83ab70f90937e34b6a Mon Sep 17 00:00:00 2001 From: haebyun Date: Thu, 5 Feb 2026 11:56:27 +0900 Subject: [PATCH 16/16] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1=20=EB=B6=84=EB=A6=AC=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20Config=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 32 ++----------------- .../baseball/config/BaseballGameConfig.java | 31 ++++++++++++++++++ 2 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 src/main/java/game/baseball/config/BaseballGameConfig.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 6f15fb29..162338ff 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,37 +1,9 @@ -import game.GamePrinter; import game.GameRunner; -import game.GamingConsole; -import game.baseball.adapter.RandomNumberGenerator; -import game.baseball.adapter.in.BaseballGameController; -import game.baseball.adapter.in.BaseballGameInputView; -import game.baseball.adapter.in.BaseballGameOutputView; -import game.baseball.adapter.out.BaseballPrinter; -import game.baseball.application.BaseballGameService; -import game.baseball.application.port.in.BaseballGameUseCase; -import game.baseball.application.port.out.GameInputPort; -import game.baseball.application.port.out.GameOutputPort; -import game.baseball.application.port.out.NumberGeneratorPort; +import game.baseball.config.BaseballGameConfig; public class Application { public static void main(String[] args) { - NumberGeneratorPort numberGeneratorPort = new RandomNumberGenerator(); - GameInputPort inputPort = new BaseballGameInputView(); - GamingConsole console = getGamingConsole(numberGeneratorPort, inputPort); - GamePrinter printer = new BaseballPrinter(); - GameRunner gameRunner = new GameRunner( - console, - printer - ); + GameRunner gameRunner = new BaseballGameConfig().gameRunner(); gameRunner.run(); } - - private static GamingConsole getGamingConsole(NumberGeneratorPort numberGeneratorPort, GameInputPort inputPort) { - GameOutputPort outputPort = new BaseballGameOutputView(); - BaseballGameUseCase useCase = new BaseballGameService(numberGeneratorPort); - return new BaseballGameController( - useCase, - inputPort, - outputPort - ); - } } diff --git a/src/main/java/game/baseball/config/BaseballGameConfig.java b/src/main/java/game/baseball/config/BaseballGameConfig.java new file mode 100644 index 00000000..7fb30495 --- /dev/null +++ b/src/main/java/game/baseball/config/BaseballGameConfig.java @@ -0,0 +1,31 @@ +package game.baseball.config; + +import game.GamePrinter; +import game.GameRunner; +import game.GamingConsole; +import game.baseball.adapter.RandomNumberGenerator; +import game.baseball.adapter.in.BaseballGameController; +import game.baseball.adapter.in.BaseballGameInputView; +import game.baseball.adapter.in.BaseballGameOutputView; +import game.baseball.adapter.out.BaseballPrinter; +import game.baseball.application.BaseballGameService; +import game.baseball.application.port.in.BaseballGameUseCase; +import game.baseball.application.port.out.GameInputPort; +import game.baseball.application.port.out.GameOutputPort; +import game.baseball.application.port.out.NumberGeneratorPort; + +public class BaseballGameConfig { + public GameRunner gameRunner() { + NumberGeneratorPort numberGeneratorPort = new RandomNumberGenerator(); + GameInputPort inputPort = new BaseballGameInputView(); + GameOutputPort outputPort = new BaseballGameOutputView(); + BaseballGameUseCase useCase = new BaseballGameService(numberGeneratorPort); + GamingConsole console = new BaseballGameController( + useCase, + inputPort, + outputPort + ); + GamePrinter printer = new BaseballPrinter(); + return new GameRunner(console, printer); + } +}