From 4bb7e56f434a74b62bbe3099cbf8284176509670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 16:31:19 +0900 Subject: [PATCH 01/12] docs(README): add new contents Add contents - feature list - class list --- docs/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/README.md b/docs/README.md index e69de29bb2..cd2c13d9af 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,24 @@ +## 기능 명세 +1. 랜덤 정답 생성 기능 + - 정답 저장 기능 +2. 사용자 정답 입력 기능 + - input 숫자 여부 검증 및 예외처리 + - input 숫자의 길이 검증 및 예외처리 +3. Strike와 Ball 개수 판단 기능 + - 3 Strike인 경우 게임 종료 기능 + - 3 Strike가 아닌 경우 결과 출력 기능 +4. 정답인 경우 새 게임 진행 여부 입력 기능 + - input 1 or 2 검증 및 예외처리 + - input 1인 경우 새 게임 시작 기능 + - input 0인 경우 종료 기능 + +## 구현 클래스 명세 + +### Application +- (main) 어플리케이션의 실행 + +### BaseballController + +### BaseballService + +### BaseballRepository From e71e02b7070d8d1a6265835ec410c45d36607cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 18:53:51 +0900 Subject: [PATCH 02/12] feat(UserAttemptValidator): add validator Add exception handling - regex - blank string - string length --- docs/README.md | 34 ++++++++++++++++--- .../java/baseball/UserAttemptValidator.java | 15 ++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 src/main/java/baseball/UserAttemptValidator.java diff --git a/docs/README.md b/docs/README.md index cd2c13d9af..7042315724 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,11 +14,37 @@ ## 구현 클래스 명세 -### Application +### Application - 실행을 담당 - (main) 어플리케이션의 실행 -### BaseballController +### BaseballController - 정답 관련 요청과 입력 검증을 담당 +- (startGame) 하나의 게임 실행 +- (validateUserAttempt) 유저의 시도 마다 입력값 검증 -### BaseballService +### BaseballService - 정답 관련 서비스 로직 담당 +- (saveAnswer) 정답을 생성하여 저장 -### BaseballRepository +### BaseballRepository - 정답 정보 저장, 조회를 담당 +- (saveAnswer) 정답 저장 + +### Answer - 정답 정보를 가진 불변클래스 +- (generateRandomNumbers) 랜덤한 숫자를 생성 +- (createAnswer) 불변클래스의 팩토리 메서드 + +### ComponentFactory - 컴포넌트들의 생성을 담당 +- (baseballController) baseballController 생성 +- (baseballService) baseballService 생성 +- (baseballRepository) baseballRepository 생성 +- (userAttemptValidator) userAttemptValidator 생성 + +### UserAttemptValidator - 유저의 시도에 대한 입력값 검증 +- (validate) 유저의 시도에 대한 입력값 검증 +- (notValidString) 입력 String 이 유효한지 검증 + +## 구현 열거형 명세 + +### GameMessage +- 게임의 메시지들을 나타낸다. + +### RandomNumberRange +- 랜덤한 숫자의 범위, 자릿수를 나타낸다. diff --git a/src/main/java/baseball/UserAttemptValidator.java b/src/main/java/baseball/UserAttemptValidator.java new file mode 100644 index 0000000000..c624532931 --- /dev/null +++ b/src/main/java/baseball/UserAttemptValidator.java @@ -0,0 +1,15 @@ +package baseball; + +public class UserAttemptValidator { + private static final String NUMERIC_MATCHER = "-?\\d+"; + + public void validate(final String userAttempt) { + if (notValidString(userAttempt)) { + throw new IllegalArgumentException(); + } + } + + private boolean notValidString(final String userAttempt) { + return userAttempt.isBlank() || !userAttempt.matches(NUMERIC_MATCHER) || userAttempt.length() != 3; + } +} From c2f6d6e5ec3280a2cffc0cba005f189da56e7765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 18:54:53 +0900 Subject: [PATCH 03/12] feat: add enum Add new enum - game message - random number range --- src/main/java/baseball/GameMessage.java | 18 ++++++++++++++++++ src/main/java/baseball/RandomNumberRange.java | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/main/java/baseball/GameMessage.java create mode 100644 src/main/java/baseball/RandomNumberRange.java diff --git a/src/main/java/baseball/GameMessage.java b/src/main/java/baseball/GameMessage.java new file mode 100644 index 0000000000..d9d2c68de8 --- /dev/null +++ b/src/main/java/baseball/GameMessage.java @@ -0,0 +1,18 @@ +package baseball; + +public enum GameMessage { + GAME_START("숫자 야구 게임을 시작합니다."), + INPUT_NEXT_NUMBER("숫자를 입력해주세요 : "), + GAME_FINISH("3개의 숫자를 모두 맞히셨습니다! 게임 종료"), + CREATE_NEW_GAME_OR_NOT("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.") + ; + private final String message; + + GameMessage(final String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/baseball/RandomNumberRange.java b/src/main/java/baseball/RandomNumberRange.java new file mode 100644 index 0000000000..61340e7b15 --- /dev/null +++ b/src/main/java/baseball/RandomNumberRange.java @@ -0,0 +1,17 @@ +package baseball; + +public enum RandomNumberRange { + MIN(1), + MAX(9), + CIPHER(3); + + private final Integer num; + + RandomNumberRange(Integer num) { + this.num = num; + } + + public Integer getNum() { + return num; + } +} From 25db36d93eb95a02ab02e26fad37bc1eb20921cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 18:56:36 +0900 Subject: [PATCH 04/12] feat: add start game feature Add new feature - start game - random number create - component factory --- src/main/java/baseball/Answer.java | 25 ++++++++++++++++++ src/main/java/baseball/Application.java | 8 +++++- .../java/baseball/BaseballController.java | 26 +++++++++++++++++++ .../java/baseball/BaseballRepository.java | 8 ++++++ src/main/java/baseball/BaseballService.java | 14 ++++++++++ src/main/java/baseball/ComponentFactory.java | 20 ++++++++++++++ 6 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/main/java/baseball/Answer.java create mode 100644 src/main/java/baseball/BaseballController.java create mode 100644 src/main/java/baseball/BaseballRepository.java create mode 100644 src/main/java/baseball/BaseballService.java create mode 100644 src/main/java/baseball/ComponentFactory.java diff --git a/src/main/java/baseball/Answer.java b/src/main/java/baseball/Answer.java new file mode 100644 index 0000000000..6a9c5c8afa --- /dev/null +++ b/src/main/java/baseball/Answer.java @@ -0,0 +1,25 @@ +package baseball; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.Collections; +import java.util.List; + +public final class Answer { + private final List currentAnswer; + + private Answer() { + this.currentAnswer = Collections.unmodifiableList(generateRandomNumbers()); + } + + private List generateRandomNumbers() { + return Randoms.pickUniqueNumbersInRange( + RandomNumberRange.MIN.getNum(), + RandomNumberRange.MAX.getNum(), + RandomNumberRange.CIPHER.getNum()); + } + + public static Answer createAnswer() { + return new Answer(); + } +} diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java index dd95a34214..e25abeb801 100644 --- a/src/main/java/baseball/Application.java +++ b/src/main/java/baseball/Application.java @@ -2,6 +2,12 @@ public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + final ComponentFactory componentFactory = new ComponentFactory(); + final BaseballController baseballController = componentFactory.baseballController(); + + boolean isContinue = Boolean.TRUE; + while(isContinue) { + baseballController.startGame(); + } } } diff --git a/src/main/java/baseball/BaseballController.java b/src/main/java/baseball/BaseballController.java new file mode 100644 index 0000000000..01870da191 --- /dev/null +++ b/src/main/java/baseball/BaseballController.java @@ -0,0 +1,26 @@ +package baseball; + +import camp.nextstep.edu.missionutils.Console; + +public class BaseballController { + private final BaseballService baseballService; + private final UserAttemptValidator userAttemptValidator; + + public BaseballController(final BaseballService baseballService, final UserAttemptValidator userAttemptValidator) { + this.baseballService = baseballService; + this.userAttemptValidator = userAttemptValidator; + } + + public void startGame() { + baseballService.saveAnswer(); + final boolean isCorrect = Boolean.TRUE; + while(isCorrect) { + final String userAttempt = Console.readLine(); + validateUserAttempt(userAttempt); + } + } + + private void validateUserAttempt(final String userAttempt) { + userAttemptValidator.validate(userAttempt); + } +} diff --git a/src/main/java/baseball/BaseballRepository.java b/src/main/java/baseball/BaseballRepository.java new file mode 100644 index 0000000000..9c3024b3e0 --- /dev/null +++ b/src/main/java/baseball/BaseballRepository.java @@ -0,0 +1,8 @@ +package baseball; + +public class BaseballRepository { + private Answer answer; + public void saveAnswer(final Answer answer) { + this.answer = answer; + } +} diff --git a/src/main/java/baseball/BaseballService.java b/src/main/java/baseball/BaseballService.java new file mode 100644 index 0000000000..a6a8b7a15b --- /dev/null +++ b/src/main/java/baseball/BaseballService.java @@ -0,0 +1,14 @@ +package baseball; + +public class BaseballService { + private final BaseballRepository baseballRepository; + + public BaseballService(final BaseballRepository baseballRepository) { + this.baseballRepository = baseballRepository; + } + + public void saveAnswer() { + final Answer answer = Answer.createAnswer(); + baseballRepository.saveAnswer(answer); + } +} diff --git a/src/main/java/baseball/ComponentFactory.java b/src/main/java/baseball/ComponentFactory.java new file mode 100644 index 0000000000..e51635ccf3 --- /dev/null +++ b/src/main/java/baseball/ComponentFactory.java @@ -0,0 +1,20 @@ +package baseball; + +public class ComponentFactory { + + public BaseballController baseballController() { + return new BaseballController(baseballService(), userAttemptValidator()); + } + + private BaseballService baseballService() { + return new BaseballService(baseballRepository()); + } + + private BaseballRepository baseballRepository() { + return new BaseballRepository(); + } + + private UserAttemptValidator userAttemptValidator() { + return new UserAttemptValidator(); + } +} From 70f7732b5138923199127d7f96c4585bf0f490d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 20:28:46 +0900 Subject: [PATCH 05/12] feat(ResultProvider): add result provide feature Add new classes and enums - ResultProvider class - CountProvider class - BallCount enum - StrikeBall enum --- docs/README.md | 19 ++++++++ src/main/java/baseball/Answer.java | 4 ++ src/main/java/baseball/BallCount.java | 30 ++++++++++++ src/main/java/baseball/CountProvider.java | 8 +++ src/main/java/baseball/ResultProvider.java | 57 ++++++++++++++++++++++ src/main/java/baseball/StrikeBall.java | 5 ++ 6 files changed, 123 insertions(+) create mode 100644 src/main/java/baseball/BallCount.java create mode 100644 src/main/java/baseball/CountProvider.java create mode 100644 src/main/java/baseball/ResultProvider.java create mode 100644 src/main/java/baseball/StrikeBall.java diff --git a/docs/README.md b/docs/README.md index 7042315724..4c84625d29 100644 --- a/docs/README.md +++ b/docs/README.md @@ -41,6 +41,18 @@ - (validate) 유저의 시도에 대한 입력값 검증 - (notValidString) 입력 String 이 유효한지 검증 +### CountProvider - 스트림에서 스트라이크, 볼의 개수를 세는 함수형 인터페이스 +- (countOf) Stream 를 받아 Integer 를 반환 + +### ResultProvider - Answer 객체와 유저의 입력을 받아 결과를 반환 +- (getResult) 최종 출력할 결과를 반환 +- (countOfStrikeBall) 스트라이크, 볼의 개수를 계산 +- (getStrikeBallStream) 입력값을 StrikeBall 으로 변환한 스트림을 반환 +- (matches) 입력값 한 자리를 인덱스와 함께 비교해 strike, ball 여부를 판단 +- (checkBallOrNone) 같은 인덱스에 같은 값이 없는 경우 ball 인지 판단 +- (findAnswerNumByIndex) 정답 숫자에서 인덱스에 해당하는 숫자 반환 +- (convertToIntWithIndex) String 에서 인덱스에 해당하는 숫자를 int 로 변환 + ## 구현 열거형 명세 ### GameMessage @@ -48,3 +60,10 @@ ### RandomNumberRange - 랜덤한 숫자의 범위, 자릿수를 나타낸다. + +### StrikeBall +- 스트라이크, 볼을 나타낸다. + +### BallCount +- 스트라이크, 볼의 개수에 따른 볼카운트를 계산한다. +- 상황에 맞는 메시지를 갖는다. diff --git a/src/main/java/baseball/Answer.java b/src/main/java/baseball/Answer.java index 6a9c5c8afa..062925b516 100644 --- a/src/main/java/baseball/Answer.java +++ b/src/main/java/baseball/Answer.java @@ -19,6 +19,10 @@ private List generateRandomNumbers() { RandomNumberRange.CIPHER.getNum()); } + public Integer findByIndex(final Integer index) { + return currentAnswer.get(index); + } + public static Answer createAnswer() { return new Answer(); } diff --git a/src/main/java/baseball/BallCount.java b/src/main/java/baseball/BallCount.java new file mode 100644 index 0000000000..ca62a1d947 --- /dev/null +++ b/src/main/java/baseball/BallCount.java @@ -0,0 +1,30 @@ +package baseball; + +import java.util.Arrays; +import java.util.function.BiPredicate; + +public enum BallCount { + THREE_STRIKE((s, b) -> s == 3, "3스트라이크"), + ONE_BALL_TWO_STRIKE((s, b) -> b == 1 && s == 2, "1볼 2스트라이크"), + ONE_BALL_ONE_STRIKE((s, b) -> b == 1 && s == 1, "1볼 1스트라이크"), + ONE_BALL((s, b) -> b == 1 && s == 0, "1볼"), + TWO_BALL_ONE_STRIKE((s, b) -> b == 2 && s == 1, "2볼 1스트라이크"), + TWO_BALL((s, b) -> b == 2 && s == 0, "2볼"), + NOTHING((s, b) -> b == 0 && s == 0, "낫싱"); + + private final BiPredicate strikeBallNumMatcher; + private final String message; + + BallCount(final BiPredicate strikeBallNumMatcher, final String message) { + this.strikeBallNumMatcher = strikeBallNumMatcher; + this.message = message; + } + + public static String findMessageByStrikeNum(final Integer ballNum, final Integer strikeNum) { + return Arrays.stream(BallCount.values()) + .filter(ballCount -> ballCount.strikeBallNumMatcher.test(strikeNum, ballNum)) + .findFirst() + .map(ballCount -> ballCount.message) + .orElseThrow(IllegalArgumentException::new); + } +} diff --git a/src/main/java/baseball/CountProvider.java b/src/main/java/baseball/CountProvider.java new file mode 100644 index 0000000000..77fdf6f510 --- /dev/null +++ b/src/main/java/baseball/CountProvider.java @@ -0,0 +1,8 @@ +package baseball; + +import java.util.stream.Stream; + +@FunctionalInterface +public interface CountProvider { + Integer countOf(Stream strikeBall); +} diff --git a/src/main/java/baseball/ResultProvider.java b/src/main/java/baseball/ResultProvider.java new file mode 100644 index 0000000000..b2b846e715 --- /dev/null +++ b/src/main/java/baseball/ResultProvider.java @@ -0,0 +1,57 @@ +package baseball; + +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class ResultProvider { + private final CountProvider strikeCountProvider = s -> s.filter(i -> i == StrikeBall.STRIKE).mapToInt(i -> 1).sum(); + private final CountProvider ballCountProvider = s -> s.filter(i -> i == StrikeBall.BALL).mapToInt(i -> 1).sum(); + private final Answer answer; + private final String attempt; + + public ResultProvider(final Answer answer, final String attempt) { + this.answer = answer; + this.attempt = attempt; + } + + public String getResult() { + final Stream strikeBallStream = getStrikeBallStream(); + final Integer strikeNum = countOfStrikeBall(strikeCountProvider, strikeBallStream); + final Integer ballNum = countOfStrikeBall(ballCountProvider, strikeBallStream); + return BallCount.findMessageByStrikeNum(ballNum, strikeNum); + } + + private Integer countOfStrikeBall(final CountProvider countProvider, final Stream strikeBallStream) { + return countProvider.countOf(strikeBallStream); + } + + private Stream getStrikeBallStream() { + return IntStream.range(0, 3).mapToObj(i -> { + final int attemptNum = convertToIntWithIndex(i); + final int answerNum = findAnswerNumByIndex(i); + return matches(answerNum, attemptNum, i); + }); + } + + private StrikeBall matches(final int answerNum, final int attemptNum, final int index) { + if (answerNum == attemptNum && answerNum == index) { + return StrikeBall.STRIKE; + } + return checkBallOrNone(answerNum, attemptNum); + } + + private StrikeBall checkBallOrNone(final int answerNum, final int attemptNum) { + if (answerNum == attemptNum) { + return StrikeBall.BALL; + } + return StrikeBall.NONE; + } + + private int findAnswerNumByIndex(final int index) { + return answer.findByIndex(index); + } + + private int convertToIntWithIndex(final int stringIndex) { + return Integer.parseInt(String.valueOf(attempt.charAt(stringIndex))); + } +} diff --git a/src/main/java/baseball/StrikeBall.java b/src/main/java/baseball/StrikeBall.java new file mode 100644 index 0000000000..636b569d4b --- /dev/null +++ b/src/main/java/baseball/StrikeBall.java @@ -0,0 +1,5 @@ +package baseball; + +public enum StrikeBall { + STRIKE, BALL, NONE +} From bd1ed674d863971014cd32c5a5a29a5d614bb664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 21:19:33 +0900 Subject: [PATCH 06/12] feat: add new game feature Add new game feature - ask to continue logic - connect calculating result flow --- docs/README.md | 9 ++++-- src/main/java/baseball/Answer.java | 4 +++ src/main/java/baseball/Application.java | 13 ++++++++ src/main/java/baseball/BallCount.java | 12 ++++++-- .../java/baseball/BaseballController.java | 23 ++++++++++---- .../java/baseball/BaseballRepository.java | 4 +++ src/main/java/baseball/BaseballService.java | 10 +++++++ src/main/java/baseball/ComponentFactory.java | 4 +-- src/main/java/baseball/InputValidator.java | 30 +++++++++++++++++++ src/main/java/baseball/ResultProvider.java | 21 +++++++------ .../java/baseball/UserAttemptValidator.java | 15 ---------- 11 files changed, 106 insertions(+), 39 deletions(-) create mode 100644 src/main/java/baseball/InputValidator.java delete mode 100644 src/main/java/baseball/UserAttemptValidator.java diff --git a/docs/README.md b/docs/README.md index 4c84625d29..e3dc620afc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,6 +16,7 @@ ### Application - 실행을 담당 - (main) 어플리케이션의 실행 +- (askContinue) 게임 종료 후 계속 진행 여부를 묻는 메서드 ### BaseballController - 정답 관련 요청과 입력 검증을 담당 - (startGame) 하나의 게임 실행 @@ -23,21 +24,25 @@ ### BaseballService - 정답 관련 서비스 로직 담당 - (saveAnswer) 정답을 생성하여 저장 +- (calculateResult) 결과를 계산 +- (findCurrentAnswer) 현재 정답 정보를 조회 ### BaseballRepository - 정답 정보 저장, 조회를 담당 - (saveAnswer) 정답 저장 +- (findCurrentAnswer) 현재 정답 정보를 조회 ### Answer - 정답 정보를 가진 불변클래스 - (generateRandomNumbers) 랜덤한 숫자를 생성 - (createAnswer) 불변클래스의 팩토리 메서드 +- (hasNum) 매개변수로 받는 숫자가 정답에 포함됐는지 확인 ### ComponentFactory - 컴포넌트들의 생성을 담당 - (baseballController) baseballController 생성 - (baseballService) baseballService 생성 - (baseballRepository) baseballRepository 생성 -- (userAttemptValidator) userAttemptValidator 생성 +- (inputValidator) inputValidator 생성 -### UserAttemptValidator - 유저의 시도에 대한 입력값 검증 +### InputValidator - 유저의 시도에 대한 입력값 검증 - (validate) 유저의 시도에 대한 입력값 검증 - (notValidString) 입력 String 이 유효한지 검증 diff --git a/src/main/java/baseball/Answer.java b/src/main/java/baseball/Answer.java index 062925b516..73d45af494 100644 --- a/src/main/java/baseball/Answer.java +++ b/src/main/java/baseball/Answer.java @@ -26,4 +26,8 @@ public Integer findByIndex(final Integer index) { public static Answer createAnswer() { return new Answer(); } + + public boolean hasNum(final int attemptNum) { + return currentAnswer.contains(attemptNum); + } } diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java index e25abeb801..c738393885 100644 --- a/src/main/java/baseball/Application.java +++ b/src/main/java/baseball/Application.java @@ -1,6 +1,10 @@ package baseball; +import camp.nextstep.edu.missionutils.Console; + public class Application { + private static final int CONTINUE_GAME = 1; + public static void main(String[] args) { final ComponentFactory componentFactory = new ComponentFactory(); final BaseballController baseballController = componentFactory.baseballController(); @@ -8,6 +12,15 @@ public static void main(String[] args) { boolean isContinue = Boolean.TRUE; while(isContinue) { baseballController.startGame(); + isContinue = askIsContinue(); } } + + private static boolean askIsContinue() { + System.out.println(GameMessage.CREATE_NEW_GAME_OR_NOT.getMessage()); + final String input = Console.readLine(); + final InputValidator inputValidator = new InputValidator(); + inputValidator.validateIsContinue(input); + return Integer.parseInt(input) == CONTINUE_GAME; + } } diff --git a/src/main/java/baseball/BallCount.java b/src/main/java/baseball/BallCount.java index ca62a1d947..d338ae10c4 100644 --- a/src/main/java/baseball/BallCount.java +++ b/src/main/java/baseball/BallCount.java @@ -5,11 +5,14 @@ public enum BallCount { THREE_STRIKE((s, b) -> s == 3, "3스트라이크"), + TWO_STRIKE((s, b) -> b == 0 && s == 2 , "2스트라이크"), ONE_BALL_TWO_STRIKE((s, b) -> b == 1 && s == 2, "1볼 2스트라이크"), ONE_BALL_ONE_STRIKE((s, b) -> b == 1 && s == 1, "1볼 1스트라이크"), - ONE_BALL((s, b) -> b == 1 && s == 0, "1볼"), TWO_BALL_ONE_STRIKE((s, b) -> b == 2 && s == 1, "2볼 1스트라이크"), + ONE_STRIKE((s, b) -> b == 0 && s == 1 , "1스트라이크"), + THREE_BALL((s, b) -> b == 3 && s == 0, "3볼"), TWO_BALL((s, b) -> b == 2 && s == 0, "2볼"), + ONE_BALL((s, b) -> b == 1 && s == 0, "1볼"), NOTHING((s, b) -> b == 0 && s == 0, "낫싱"); private final BiPredicate strikeBallNumMatcher; @@ -20,11 +23,14 @@ public enum BallCount { this.message = message; } - public static String findMessageByStrikeNum(final Integer ballNum, final Integer strikeNum) { + public static BallCount ofStrikeAndBallNum(final Integer ballNum, final Integer strikeNum) { return Arrays.stream(BallCount.values()) .filter(ballCount -> ballCount.strikeBallNumMatcher.test(strikeNum, ballNum)) .findFirst() - .map(ballCount -> ballCount.message) .orElseThrow(IllegalArgumentException::new); } + + public String getMessage() { + return this.message; + } } diff --git a/src/main/java/baseball/BaseballController.java b/src/main/java/baseball/BaseballController.java index 01870da191..fe55324115 100644 --- a/src/main/java/baseball/BaseballController.java +++ b/src/main/java/baseball/BaseballController.java @@ -4,23 +4,34 @@ public class BaseballController { private final BaseballService baseballService; - private final UserAttemptValidator userAttemptValidator; + private final InputValidator inputValidator; - public BaseballController(final BaseballService baseballService, final UserAttemptValidator userAttemptValidator) { + public BaseballController(final BaseballService baseballService, final InputValidator inputValidator) { this.baseballService = baseballService; - this.userAttemptValidator = userAttemptValidator; + this.inputValidator = inputValidator; } public void startGame() { + System.out.println(GameMessage.GAME_START.getMessage()); baseballService.saveAnswer(); - final boolean isCorrect = Boolean.TRUE; - while(isCorrect) { + + boolean isContinue = Boolean.TRUE; + while(isContinue) { + System.out.print(GameMessage.INPUT_NEXT_NUMBER.getMessage()); final String userAttempt = Console.readLine(); validateUserAttempt(userAttempt); + + final BallCount result = baseballService.calculateResult(userAttempt); + System.out.println(result.getMessage()); + + if (result == BallCount.THREE_STRIKE) { + System.out.println(GameMessage.GAME_FINISH.getMessage()); + isContinue = Boolean.FALSE; + } } } private void validateUserAttempt(final String userAttempt) { - userAttemptValidator.validate(userAttempt); + inputValidator.validateUserAttempt(userAttempt); } } diff --git a/src/main/java/baseball/BaseballRepository.java b/src/main/java/baseball/BaseballRepository.java index 9c3024b3e0..99cade22e3 100644 --- a/src/main/java/baseball/BaseballRepository.java +++ b/src/main/java/baseball/BaseballRepository.java @@ -5,4 +5,8 @@ public class BaseballRepository { public void saveAnswer(final Answer answer) { this.answer = answer; } + + public Answer findCurrentAnswer() { + return answer; + } } diff --git a/src/main/java/baseball/BaseballService.java b/src/main/java/baseball/BaseballService.java index a6a8b7a15b..126d431e06 100644 --- a/src/main/java/baseball/BaseballService.java +++ b/src/main/java/baseball/BaseballService.java @@ -11,4 +11,14 @@ public void saveAnswer() { final Answer answer = Answer.createAnswer(); baseballRepository.saveAnswer(answer); } + + public BallCount calculateResult(String attempt) { + final Answer currentAnswer = findCurrentAnswer(); + final ResultProvider resultProvider = new ResultProvider(currentAnswer, attempt); + return resultProvider.getResult(); + } + + private Answer findCurrentAnswer() { + return baseballRepository.findCurrentAnswer(); + } } diff --git a/src/main/java/baseball/ComponentFactory.java b/src/main/java/baseball/ComponentFactory.java index e51635ccf3..26804dc413 100644 --- a/src/main/java/baseball/ComponentFactory.java +++ b/src/main/java/baseball/ComponentFactory.java @@ -14,7 +14,7 @@ private BaseballRepository baseballRepository() { return new BaseballRepository(); } - private UserAttemptValidator userAttemptValidator() { - return new UserAttemptValidator(); + private InputValidator userAttemptValidator() { + return new InputValidator(); } } diff --git a/src/main/java/baseball/InputValidator.java b/src/main/java/baseball/InputValidator.java new file mode 100644 index 0000000000..2c852fe9c3 --- /dev/null +++ b/src/main/java/baseball/InputValidator.java @@ -0,0 +1,30 @@ +package baseball; + +public class InputValidator { + private static final String NUMERIC_MATCHER = "-?\\d+"; + + public void validateUserAttempt(final String userAttempt) { + if (notValidAttemptString(userAttempt) || hasDuplicate(userAttempt)) { + throw new IllegalArgumentException(); + } + } + + private boolean notValidAttemptString(final String userAttempt) { + return userAttempt.isBlank() || !userAttempt.matches(NUMERIC_MATCHER) || userAttempt.length() != 3; + } + + private boolean hasDuplicate(final String userAttempt) { + final long count = userAttempt.chars().distinct().count(); + return count != 3; + } + + public void validateIsContinue(final String isContinueInput) { + if (notValidIsContinueString(isContinueInput)) { + throw new IllegalArgumentException(); + } + } + + private boolean notValidIsContinueString(final String isContinueInput) { + return isContinueInput.isBlank() || !isContinueInput.matches(NUMERIC_MATCHER) || isContinueInput.length() != 1; + } +} diff --git a/src/main/java/baseball/ResultProvider.java b/src/main/java/baseball/ResultProvider.java index b2b846e715..fd91ada33d 100644 --- a/src/main/java/baseball/ResultProvider.java +++ b/src/main/java/baseball/ResultProvider.java @@ -4,8 +4,8 @@ import java.util.stream.Stream; public class ResultProvider { - private final CountProvider strikeCountProvider = s -> s.filter(i -> i == StrikeBall.STRIKE).mapToInt(i -> 1).sum(); - private final CountProvider ballCountProvider = s -> s.filter(i -> i == StrikeBall.BALL).mapToInt(i -> 1).sum(); + private final CountProvider strikeCountProvider = s -> Long.valueOf(s.filter(i -> i == StrikeBall.STRIKE).count()).intValue(); + private final CountProvider ballCountProvider = s -> Long.valueOf(s.filter(i -> i == StrikeBall.BALL).count()).intValue(); private final Answer answer; private final String attempt; @@ -14,11 +14,10 @@ public ResultProvider(final Answer answer, final String attempt) { this.attempt = attempt; } - public String getResult() { - final Stream strikeBallStream = getStrikeBallStream(); - final Integer strikeNum = countOfStrikeBall(strikeCountProvider, strikeBallStream); - final Integer ballNum = countOfStrikeBall(ballCountProvider, strikeBallStream); - return BallCount.findMessageByStrikeNum(ballNum, strikeNum); + public BallCount getResult() { + final Integer strikeNum = countOfStrikeBall(strikeCountProvider, getStrikeBallStream()); + final Integer ballNum = countOfStrikeBall(ballCountProvider, getStrikeBallStream()); + return BallCount.ofStrikeAndBallNum(ballNum, strikeNum); } private Integer countOfStrikeBall(final CountProvider countProvider, final Stream strikeBallStream) { @@ -34,14 +33,14 @@ private Stream getStrikeBallStream() { } private StrikeBall matches(final int answerNum, final int attemptNum, final int index) { - if (answerNum == attemptNum && answerNum == index) { + if (answerNum == attemptNum && attemptNum == findAnswerNumByIndex(index)) { return StrikeBall.STRIKE; } - return checkBallOrNone(answerNum, attemptNum); + return checkBallOrNone(attemptNum); } - private StrikeBall checkBallOrNone(final int answerNum, final int attemptNum) { - if (answerNum == attemptNum) { + private StrikeBall checkBallOrNone(final int attemptNum) { + if (answer.hasNum(attemptNum)) { return StrikeBall.BALL; } return StrikeBall.NONE; diff --git a/src/main/java/baseball/UserAttemptValidator.java b/src/main/java/baseball/UserAttemptValidator.java deleted file mode 100644 index c624532931..0000000000 --- a/src/main/java/baseball/UserAttemptValidator.java +++ /dev/null @@ -1,15 +0,0 @@ -package baseball; - -public class UserAttemptValidator { - private static final String NUMERIC_MATCHER = "-?\\d+"; - - public void validate(final String userAttempt) { - if (notValidString(userAttempt)) { - throw new IllegalArgumentException(); - } - } - - private boolean notValidString(final String userAttempt) { - return userAttempt.isBlank() || !userAttempt.matches(NUMERIC_MATCHER) || userAttempt.length() != 3; - } -} From 060c5a7dd3d3ef0b0b6716bdc335c04decf563a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 22:24:26 +0900 Subject: [PATCH 07/12] test: add unit test Add unit test - ResultProviderTest - InputValidatorTest - BaseballServiceTest - BaseballRepositoryTest --- docs/README.md | 9 +- src/main/java/baseball/BaseballService.java | 2 +- src/main/java/baseball/InputValidator.java | 7 +- src/main/java/baseball/RandomNumberRange.java | 2 +- src/main/java/baseball/ResultProvider.java | 4 +- .../java/baseball/BaseballRepositoryTest.java | 25 ++++++ .../java/baseball/BaseballServiceTest.java | 82 +++++++++++++++++++ .../java/baseball/InputValidatorTest.java | 72 ++++++++++++++++ .../java/baseball/ResultProviderTest.java | 82 +++++++++++++++++++ 9 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 src/test/java/baseball/BaseballRepositoryTest.java create mode 100644 src/test/java/baseball/BaseballServiceTest.java create mode 100644 src/test/java/baseball/InputValidatorTest.java create mode 100644 src/test/java/baseball/ResultProviderTest.java diff --git a/docs/README.md b/docs/README.md index e3dc620afc..763f8b918d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -43,8 +43,11 @@ - (inputValidator) inputValidator 생성 ### InputValidator - 유저의 시도에 대한 입력값 검증 -- (validate) 유저의 시도에 대한 입력값 검증 -- (notValidString) 입력 String 이 유효한지 검증 +- (validateUserAttempt) 유저의 시도에 대한 입력값 검증 +- (notValidAttemptString) 입력 String 이 유효한지 검증 +- (hasDuplicate) 중복된 숫자가 입력되었는지 검증 +- (validateIsContinue) 계속 진행할지 여부에 대한 입력 검증 +- (notValidContinueString) 계속 진행할지 여부에 대한 입력값 검증 ### CountProvider - 스트림에서 스트라이크, 볼의 개수를 세는 함수형 인터페이스 - (countOf) Stream 를 받아 Integer 를 반환 @@ -53,7 +56,7 @@ - (getResult) 최종 출력할 결과를 반환 - (countOfStrikeBall) 스트라이크, 볼의 개수를 계산 - (getStrikeBallStream) 입력값을 StrikeBall 으로 변환한 스트림을 반환 -- (matches) 입력값 한 자리를 인덱스와 함께 비교해 strike, ball 여부를 판단 +- (checkStrikeBall) 입력값 한 자리를 인덱스와 함께 비교해 strike, ball 여부를 판단 - (checkBallOrNone) 같은 인덱스에 같은 값이 없는 경우 ball 인지 판단 - (findAnswerNumByIndex) 정답 숫자에서 인덱스에 해당하는 숫자 반환 - (convertToIntWithIndex) String 에서 인덱스에 해당하는 숫자를 int 로 변환 diff --git a/src/main/java/baseball/BaseballService.java b/src/main/java/baseball/BaseballService.java index 126d431e06..540c37fbd1 100644 --- a/src/main/java/baseball/BaseballService.java +++ b/src/main/java/baseball/BaseballService.java @@ -12,7 +12,7 @@ public void saveAnswer() { baseballRepository.saveAnswer(answer); } - public BallCount calculateResult(String attempt) { + public BallCount calculateResult(final String attempt) { final Answer currentAnswer = findCurrentAnswer(); final ResultProvider resultProvider = new ResultProvider(currentAnswer, attempt); return resultProvider.getResult(); diff --git a/src/main/java/baseball/InputValidator.java b/src/main/java/baseball/InputValidator.java index 2c852fe9c3..07eac9fa35 100644 --- a/src/main/java/baseball/InputValidator.java +++ b/src/main/java/baseball/InputValidator.java @@ -19,7 +19,7 @@ private boolean hasDuplicate(final String userAttempt) { } public void validateIsContinue(final String isContinueInput) { - if (notValidIsContinueString(isContinueInput)) { + if (notValidIsContinueString(isContinueInput) || isOutOfRange(isContinueInput)) { throw new IllegalArgumentException(); } } @@ -27,4 +27,9 @@ public void validateIsContinue(final String isContinueInput) { private boolean notValidIsContinueString(final String isContinueInput) { return isContinueInput.isBlank() || !isContinueInput.matches(NUMERIC_MATCHER) || isContinueInput.length() != 1; } + + private boolean isOutOfRange(final String isContinueInput) { + final int input = Integer.parseInt(isContinueInput); + return input != 1 && input != 2; + } } diff --git a/src/main/java/baseball/RandomNumberRange.java b/src/main/java/baseball/RandomNumberRange.java index 61340e7b15..c910ef4b65 100644 --- a/src/main/java/baseball/RandomNumberRange.java +++ b/src/main/java/baseball/RandomNumberRange.java @@ -7,7 +7,7 @@ public enum RandomNumberRange { private final Integer num; - RandomNumberRange(Integer num) { + RandomNumberRange(final Integer num) { this.num = num; } diff --git a/src/main/java/baseball/ResultProvider.java b/src/main/java/baseball/ResultProvider.java index fd91ada33d..f5df0ae837 100644 --- a/src/main/java/baseball/ResultProvider.java +++ b/src/main/java/baseball/ResultProvider.java @@ -28,11 +28,11 @@ private Stream getStrikeBallStream() { return IntStream.range(0, 3).mapToObj(i -> { final int attemptNum = convertToIntWithIndex(i); final int answerNum = findAnswerNumByIndex(i); - return matches(answerNum, attemptNum, i); + return checkStrikeBall(answerNum, attemptNum, i); }); } - private StrikeBall matches(final int answerNum, final int attemptNum, final int index) { + private StrikeBall checkStrikeBall(final int answerNum, final int attemptNum, final int index) { if (answerNum == attemptNum && attemptNum == findAnswerNumByIndex(index)) { return StrikeBall.STRIKE; } diff --git a/src/test/java/baseball/BaseballRepositoryTest.java b/src/test/java/baseball/BaseballRepositoryTest.java new file mode 100644 index 0000000000..38611c3bd4 --- /dev/null +++ b/src/test/java/baseball/BaseballRepositoryTest.java @@ -0,0 +1,25 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("BaseRepository의") +class BaseballRepositoryTest { + private final BaseballRepository baseballRepository = new BaseballRepository(); + + @Test + @DisplayName("저장후 조회 로직이 수행되는가") + void saveAnswerAndGet() { + //given + final Answer answer = Answer.createAnswer(); + + //when + baseballRepository.saveAnswer(answer); + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + + //then + assertThat(answer).isEqualTo(currentAnswer); + } +} diff --git a/src/test/java/baseball/BaseballServiceTest.java b/src/test/java/baseball/BaseballServiceTest.java new file mode 100644 index 0000000000..83dcf5f26d --- /dev/null +++ b/src/test/java/baseball/BaseballServiceTest.java @@ -0,0 +1,82 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("BaseballService 의") +class BaseballServiceTest { + private final BaseballRepository baseballRepository = new BaseballRepository(); + private final BaseballService baseballService = new BaseballService(baseballRepository); + + + @Test + @DisplayName("정답 저장 로직이 수행되는가") + void saveAnswer() { + //given + + //when + baseballService.saveAnswer(); + + //then + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + assertThat(currentAnswer).isNotNull(); + } + + @Nested + @DisplayName("결과 계산 로직 중") + class CalculateResult { + @Test + @DisplayName("3스트라이크 결과 반환이 수행되는가") + void threeStrike() { + //given + baseballService.saveAnswer(); + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + final Integer first = currentAnswer.findByIndex(0); + final Integer second = currentAnswer.findByIndex(1); + final Integer third = currentAnswer.findByIndex(2); + + //when + final BallCount ballCount = baseballService.calculateResult(Integer.toString(first) + Integer.toString(second) + Integer.toString(third)); + + //then + assertThat(ballCount).isEqualTo(BallCount.THREE_STRIKE); + } + + @Test + @DisplayName("3볼 결과 반환이 수행되는가") + void threeBall() { + //given + baseballService.saveAnswer(); + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + final Integer first = currentAnswer.findByIndex(0); + final Integer second = currentAnswer.findByIndex(1); + final Integer third = currentAnswer.findByIndex(2); + + //when + final BallCount ballCount = baseballService.calculateResult(Integer.toString(third) + Integer.toString(first) + Integer.toString(second)); + + //then + assertThat(ballCount).isEqualTo(BallCount.THREE_BALL); + } + + @Test + @DisplayName("2볼 1스트라이크 결과 반환이 수행되는가") + void oneStrikeTwoBall() { + //given + baseballService.saveAnswer(); + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + final Integer first = currentAnswer.findByIndex(0); + final Integer second = currentAnswer.findByIndex(1); + final Integer third = currentAnswer.findByIndex(2); + + //when + final BallCount ballCount = baseballService.calculateResult(Integer.toString(first) + Integer.toString(third) + Integer.toString(second)); + + //then + assertThat(ballCount).isEqualTo(BallCount.TWO_BALL_ONE_STRIKE); + } + } +} diff --git a/src/test/java/baseball/InputValidatorTest.java b/src/test/java/baseball/InputValidatorTest.java new file mode 100644 index 0000000000..07c9b52ae6 --- /dev/null +++ b/src/test/java/baseball/InputValidatorTest.java @@ -0,0 +1,72 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static camp.nextstep.edu.missionutils.test.Assertions.assertSimpleTest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("InputValidator의") +class InputValidatorTest { + private final InputValidator inputValidator = new InputValidator(); + + @Nested + @DisplayName("유저의 시도 검증 중") + class ValidateUserAttempt { + @Test + @DisplayName("입력값 길이 검증이 수행되는가") + void validateLength() { + assertThatThrownBy(() -> inputValidator.validateUserAttempt("1234")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("중복 값 검증이 수행되는가") + void validateDuplicate() { + assertThatThrownBy(() -> inputValidator.validateUserAttempt("122")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("빈 값 검증이 수행되는가") + void validateBlank() { + assertThatThrownBy(() -> inputValidator.validateUserAttempt("")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("정상 입력값 검증이 수행되는가") + void validate() { + inputValidator.validateUserAttempt("123"); + inputValidator.validateUserAttempt("275"); + } + } + + @Nested + @DisplayName("게임 이어서 하기 여부 검증 중") + class ValidateIsContinue { + @Test + @DisplayName("입력값 길이 검증이 수행되는가") + void validateLength() { + assertThatThrownBy(() -> inputValidator.validateIsContinue("1234")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("입력값 범위 검증이 수행되는가") + void validateRange() { + assertThatThrownBy(() -> inputValidator.validateIsContinue("3")) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("정상 입력값 검증이 수행되는가") + void validate() { + inputValidator.validateIsContinue("1"); + inputValidator.validateIsContinue("2"); + } + } +} diff --git a/src/test/java/baseball/ResultProviderTest.java b/src/test/java/baseball/ResultProviderTest.java new file mode 100644 index 0000000000..7a7dd34b13 --- /dev/null +++ b/src/test/java/baseball/ResultProviderTest.java @@ -0,0 +1,82 @@ +package baseball; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("ResultProvider의") +class ResultProviderTest { + private final BaseballRepository baseballRepository = new BaseballRepository(); + + @Nested + @DisplayName("결과 계산 로직 중") + class GetResult { + @Test + @DisplayName("3스트라이크가 반환되는가") + void threeStrikeResult() { + //given + final Answer answer = Answer.createAnswer(); + baseballRepository.saveAnswer(answer); + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + + final ResultProvider resultProvider = + new ResultProvider( + answer, + Integer.toString(currentAnswer.findByIndex(0)) + + Integer.toString(currentAnswer.findByIndex(1)) + + Integer.toString(currentAnswer.findByIndex(2))); + + //when + final BallCount result = resultProvider.getResult(); + + //then + assertThat(result).isEqualTo(BallCount.THREE_STRIKE); + } + + @Test + @DisplayName("3볼이 반환되는가") + void threeBallResult() { + //given + final Answer answer = Answer.createAnswer(); + baseballRepository.saveAnswer(answer); + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + + final ResultProvider resultProvider = + new ResultProvider( + answer, + Integer.toString(currentAnswer.findByIndex(2)) + + Integer.toString(currentAnswer.findByIndex(0)) + + Integer.toString(currentAnswer.findByIndex(1))); + + //when + final BallCount result = resultProvider.getResult(); + + //then + assertThat(result).isEqualTo(BallCount.THREE_BALL); + } + + @Test + @DisplayName("2볼 1스트라이크가 반환되는가") + void twoBallOneStrikeResult() { + //given + final Answer answer = Answer.createAnswer(); + baseballRepository.saveAnswer(answer); + final Answer currentAnswer = baseballRepository.findCurrentAnswer(); + + final ResultProvider resultProvider = + new ResultProvider( + answer, + Integer.toString(currentAnswer.findByIndex(0)) + + Integer.toString(currentAnswer.findByIndex(2)) + + Integer.toString(currentAnswer.findByIndex(1))); + + //when + final BallCount result = resultProvider.getResult(); + + //then + assertThat(result).isEqualTo(BallCount.TWO_BALL_ONE_STRIKE); + } + } +} From 4b944b6daf62d8bfef383a16c7d515c45f2e29bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Sat, 29 Jul 2023 23:52:33 +0900 Subject: [PATCH 08/12] refactor: divide methods in controller Refactor controller and update README --- docs/README.md | 4 +++ .../java/baseball/BaseballController.java | 32 ++++++++++++++----- src/main/java/baseball/InputValidator.java | 14 ++++++-- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/docs/README.md b/docs/README.md index 763f8b918d..68b00150e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,10 @@ ### BaseballController - 정답 관련 요청과 입력 검증을 담당 - (startGame) 하나의 게임 실행 - (validateUserAttempt) 유저의 시도 마다 입력값 검증 +- (printGameMessage) 게임 메시지 출력 +- (playGame) 게임 메인 로직 실행 +- (getResult) 결과 계산 로직 실행 +- (getUserAttempt) 유저의 정답 시도 입력 ### BaseballService - 정답 관련 서비스 로직 담당 - (saveAnswer) 정답을 생성하여 저장 diff --git a/src/main/java/baseball/BaseballController.java b/src/main/java/baseball/BaseballController.java index fe55324115..d7cb107c48 100644 --- a/src/main/java/baseball/BaseballController.java +++ b/src/main/java/baseball/BaseballController.java @@ -12,25 +12,41 @@ public BaseballController(final BaseballService baseballService, final InputVali } public void startGame() { - System.out.println(GameMessage.GAME_START.getMessage()); + printGameMessage(GameMessage.GAME_START.getMessage()); baseballService.saveAnswer(); + playGame(); + } + private void playGame() { boolean isContinue = Boolean.TRUE; while(isContinue) { - System.out.print(GameMessage.INPUT_NEXT_NUMBER.getMessage()); - final String userAttempt = Console.readLine(); - validateUserAttempt(userAttempt); - - final BallCount result = baseballService.calculateResult(userAttempt); - System.out.println(result.getMessage()); + final String userAttempt = getUserAttempt(); + final BallCount result = getResult(userAttempt); if (result == BallCount.THREE_STRIKE) { - System.out.println(GameMessage.GAME_FINISH.getMessage()); + printGameMessage(GameMessage.GAME_FINISH.getMessage()); isContinue = Boolean.FALSE; } } } + private String getUserAttempt() { + printGameMessage(GameMessage.INPUT_NEXT_NUMBER.getMessage()); + final String userAttempt = Console.readLine(); + validateUserAttempt(userAttempt); + return userAttempt; + } + + private BallCount getResult(String userAttempt) { + final BallCount result = baseballService.calculateResult(userAttempt); + printGameMessage(result.getMessage()); + return result; + } + + private void printGameMessage(final String message) { + System.out.println(message); + } + private void validateUserAttempt(final String userAttempt) { inputValidator.validateUserAttempt(userAttempt); } diff --git a/src/main/java/baseball/InputValidator.java b/src/main/java/baseball/InputValidator.java index 07eac9fa35..5d05d24b32 100644 --- a/src/main/java/baseball/InputValidator.java +++ b/src/main/java/baseball/InputValidator.java @@ -2,6 +2,10 @@ public class InputValidator { private static final String NUMERIC_MATCHER = "-?\\d+"; + private static final Integer IS_CONTINUE_LENGTH = 1; + private static final Integer CONTINUE = 1; + private static final Integer NOT_CONTINUE = 1; + public void validateUserAttempt(final String userAttempt) { if (notValidAttemptString(userAttempt) || hasDuplicate(userAttempt)) { @@ -10,7 +14,9 @@ public void validateUserAttempt(final String userAttempt) { } private boolean notValidAttemptString(final String userAttempt) { - return userAttempt.isBlank() || !userAttempt.matches(NUMERIC_MATCHER) || userAttempt.length() != 3; + return userAttempt.isBlank() + || !userAttempt.matches(NUMERIC_MATCHER) + || userAttempt.length() != RandomNumberRange.CIPHER.getNum(); } private boolean hasDuplicate(final String userAttempt) { @@ -25,11 +31,13 @@ public void validateIsContinue(final String isContinueInput) { } private boolean notValidIsContinueString(final String isContinueInput) { - return isContinueInput.isBlank() || !isContinueInput.matches(NUMERIC_MATCHER) || isContinueInput.length() != 1; + return isContinueInput.isBlank() + || !isContinueInput.matches(NUMERIC_MATCHER) + || isContinueInput.length() != IS_CONTINUE_LENGTH; } private boolean isOutOfRange(final String isContinueInput) { final int input = Integer.parseInt(isContinueInput); - return input != 1 && input != 2; + return input != CONTINUE && input != NOT_CONTINUE; } } From 96a4c3102d24485ee6c2ce2841b4a7c77c01fed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Thu, 3 Aug 2023 18:00:40 +0900 Subject: [PATCH 09/12] refactor: move start logic add reader and writer Refactor io and starting logic - add InputReader - add OutputWriter - move game start logic --- src/main/java/baseball/Application.java | 19 +------ src/main/java/baseball/BallCount.java | 3 +- .../java/baseball/BaseballController.java | 49 ++++++++++++------- src/main/java/baseball/ComponentFactory.java | 14 +++++- src/main/java/baseball/GameMessage.java | 5 +- src/main/java/baseball/InputReader.java | 9 ++++ src/main/java/baseball/OutputWriter.java | 15 ++++++ 7 files changed, 75 insertions(+), 39 deletions(-) create mode 100644 src/main/java/baseball/InputReader.java create mode 100644 src/main/java/baseball/OutputWriter.java diff --git a/src/main/java/baseball/Application.java b/src/main/java/baseball/Application.java index c738393885..75c1ab3827 100644 --- a/src/main/java/baseball/Application.java +++ b/src/main/java/baseball/Application.java @@ -1,26 +1,9 @@ package baseball; -import camp.nextstep.edu.missionutils.Console; - public class Application { - private static final int CONTINUE_GAME = 1; - public static void main(String[] args) { final ComponentFactory componentFactory = new ComponentFactory(); final BaseballController baseballController = componentFactory.baseballController(); - - boolean isContinue = Boolean.TRUE; - while(isContinue) { - baseballController.startGame(); - isContinue = askIsContinue(); - } - } - - private static boolean askIsContinue() { - System.out.println(GameMessage.CREATE_NEW_GAME_OR_NOT.getMessage()); - final String input = Console.readLine(); - final InputValidator inputValidator = new InputValidator(); - inputValidator.validateIsContinue(input); - return Integer.parseInt(input) == CONTINUE_GAME; + baseballController.startGame(); } } diff --git a/src/main/java/baseball/BallCount.java b/src/main/java/baseball/BallCount.java index d338ae10c4..33280f7f55 100644 --- a/src/main/java/baseball/BallCount.java +++ b/src/main/java/baseball/BallCount.java @@ -30,7 +30,8 @@ public static BallCount ofStrikeAndBallNum(final Integer ballNum, final Integer .orElseThrow(IllegalArgumentException::new); } - public String getMessage() { + @Override + public String toString() { return this.message; } } diff --git a/src/main/java/baseball/BaseballController.java b/src/main/java/baseball/BaseballController.java index d7cb107c48..9cb91c4335 100644 --- a/src/main/java/baseball/BaseballController.java +++ b/src/main/java/baseball/BaseballController.java @@ -1,52 +1,67 @@ package baseball; -import camp.nextstep.edu.missionutils.Console; - public class BaseballController { + private static final int CONTINUE_COMMAND = 1; private final BaseballService baseballService; private final InputValidator inputValidator; + private final OutputWriter outputWriter; + private final InputReader inputReader; + + public BaseballController( + final BaseballService baseballService, + final InputValidator inputValidator, + final OutputWriter outputWriter, + final InputReader inputReader) { - public BaseballController(final BaseballService baseballService, final InputValidator inputValidator) { this.baseballService = baseballService; this.inputValidator = inputValidator; + this.outputWriter = outputWriter; + this.inputReader = inputReader; } public void startGame() { - printGameMessage(GameMessage.GAME_START.getMessage()); - baseballService.saveAnswer(); - playGame(); + boolean isContinue = true; + while(isContinue) { + outputWriter.printWithLine(GameMessage.GAME_START); + baseballService.saveAnswer(); + playGame(); + isContinue = askIsContinue(); + } + } + + private boolean askIsContinue() { + outputWriter.printWithLine(GameMessage.CREATE_NEW_GAME_OR_NOT); + final String input = inputReader.read(); + inputValidator.validateIsContinue(input); + return Integer.parseInt(input) == CONTINUE_COMMAND; } private void playGame() { - boolean isContinue = Boolean.TRUE; + boolean isContinue = true; while(isContinue) { final String userAttempt = getUserAttempt(); final BallCount result = getResult(userAttempt); if (result == BallCount.THREE_STRIKE) { - printGameMessage(GameMessage.GAME_FINISH.getMessage()); - isContinue = Boolean.FALSE; + outputWriter.printWithLine(GameMessage.GAME_FINISH); + isContinue = false; } } } private String getUserAttempt() { - printGameMessage(GameMessage.INPUT_NEXT_NUMBER.getMessage()); - final String userAttempt = Console.readLine(); + outputWriter.print(GameMessage.INPUT_NEXT_NUMBER); + final String userAttempt = inputReader.read(); validateUserAttempt(userAttempt); return userAttempt; } - private BallCount getResult(String userAttempt) { + private BallCount getResult(final String userAttempt) { final BallCount result = baseballService.calculateResult(userAttempt); - printGameMessage(result.getMessage()); + outputWriter.printWithLine(result); return result; } - private void printGameMessage(final String message) { - System.out.println(message); - } - private void validateUserAttempt(final String userAttempt) { inputValidator.validateUserAttempt(userAttempt); } diff --git a/src/main/java/baseball/ComponentFactory.java b/src/main/java/baseball/ComponentFactory.java index 26804dc413..39b9ffa444 100644 --- a/src/main/java/baseball/ComponentFactory.java +++ b/src/main/java/baseball/ComponentFactory.java @@ -3,7 +3,19 @@ public class ComponentFactory { public BaseballController baseballController() { - return new BaseballController(baseballService(), userAttemptValidator()); + return new BaseballController( + baseballService(), + userAttemptValidator(), + outputWriter(), + inputReader()); + } + + private InputReader inputReader() { + return new InputReader(); + } + + private OutputWriter outputWriter() { + return new OutputWriter(); } private BaseballService baseballService() { diff --git a/src/main/java/baseball/GameMessage.java b/src/main/java/baseball/GameMessage.java index d9d2c68de8..e875483839 100644 --- a/src/main/java/baseball/GameMessage.java +++ b/src/main/java/baseball/GameMessage.java @@ -12,7 +12,8 @@ public enum GameMessage { this.message = message; } - public String getMessage() { - return message; + @Override + public String toString() { + return this.message; } } diff --git a/src/main/java/baseball/InputReader.java b/src/main/java/baseball/InputReader.java new file mode 100644 index 0000000000..8216315714 --- /dev/null +++ b/src/main/java/baseball/InputReader.java @@ -0,0 +1,9 @@ +package baseball; + +import camp.nextstep.edu.missionutils.Console; + +public class InputReader { + public String read() { + return Console.readLine(); + } +} diff --git a/src/main/java/baseball/OutputWriter.java b/src/main/java/baseball/OutputWriter.java new file mode 100644 index 0000000000..0b7058ac91 --- /dev/null +++ b/src/main/java/baseball/OutputWriter.java @@ -0,0 +1,15 @@ +package baseball; + +public class OutputWriter { + public void printWithLine(final GameMessage gameMessage) { + System.out.println(gameMessage); + } + + public void print(final GameMessage gameMessage) { + System.out.print(gameMessage); + } + + public void printWithLine(final BallCount ballCount) { + System.out.println(ballCount); + } +} From 0a25bfefce6a50f38a0a2d43aa34889ee8bf8a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Thu, 3 Aug 2023 19:39:56 +0900 Subject: [PATCH 10/12] refactor: fix strike and ball count logic --- docs/README.md | 87 +++++++++++-------- src/main/java/baseball/Answer.java | 8 +- src/main/java/baseball/BallCount.java | 37 -------- .../java/baseball/BaseballController.java | 37 +++----- .../java/baseball/BaseballRepository.java | 3 +- src/main/java/baseball/BaseballService.java | 4 +- src/main/java/baseball/ComponentFactory.java | 11 ++- src/main/java/baseball/CountProvider.java | 8 -- src/main/java/baseball/GameMessage.java | 3 +- src/main/java/baseball/InputReader.java | 18 +++- src/main/java/baseball/InputValidator.java | 2 +- src/main/java/baseball/OutputWriter.java | 14 ++- src/main/java/baseball/Result.java | 19 ++++ src/main/java/baseball/ResultMessage.java | 18 ++++ .../java/baseball/ResultOutputFormatter.java | 38 ++++++++ src/main/java/baseball/ResultProvider.java | 23 +++-- .../java/baseball/BaseballServiceTest.java | 12 +-- .../java/baseball/ResultProviderTest.java | 12 +-- 18 files changed, 204 insertions(+), 150 deletions(-) delete mode 100644 src/main/java/baseball/BallCount.java delete mode 100644 src/main/java/baseball/CountProvider.java create mode 100644 src/main/java/baseball/Result.java create mode 100644 src/main/java/baseball/ResultMessage.java create mode 100644 src/main/java/baseball/ResultOutputFormatter.java diff --git a/docs/README.md b/docs/README.md index 68b00150e3..e39cb3e1c4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,55 +15,69 @@ ## 구현 클래스 명세 ### Application - 실행을 담당 -- (main) 어플리케이션의 실행 -- (askContinue) 게임 종료 후 계속 진행 여부를 묻는 메서드 +- main() 어플리케이션의 실행 ### BaseballController - 정답 관련 요청과 입력 검증을 담당 -- (startGame) 하나의 게임 실행 -- (validateUserAttempt) 유저의 시도 마다 입력값 검증 -- (printGameMessage) 게임 메시지 출력 -- (playGame) 게임 메인 로직 실행 -- (getResult) 결과 계산 로직 실행 -- (getUserAttempt) 유저의 정답 시도 입력 +- startGame() 하나의 게임 실행 +- askIsContinue() 게임 종료시 이어서 할 지에 대한 여부 검증 +- playGame() 게임 메인 로직 실행 +- getUserAttempt() 유저의 정답 시도 입력 +- getResult() 결과 계산 로직 실행 ### BaseballService - 정답 관련 서비스 로직 담당 -- (saveAnswer) 정답을 생성하여 저장 -- (calculateResult) 결과를 계산 -- (findCurrentAnswer) 현재 정답 정보를 조회 +- saveAnswer() 정답을 생성하여 저장 +- calculateResult() 결과를 계산 +- findCurrentAnswer() 현재 정답 정보를 조회 ### BaseballRepository - 정답 정보 저장, 조회를 담당 -- (saveAnswer) 정답 저장 -- (findCurrentAnswer) 현재 정답 정보를 조회 +- saveAnswer() 정답 저장 +- findCurrentAnswer() 현재 정답 정보를 조회 ### Answer - 정답 정보를 가진 불변클래스 -- (generateRandomNumbers) 랜덤한 숫자를 생성 -- (createAnswer) 불변클래스의 팩토리 메서드 -- (hasNum) 매개변수로 받는 숫자가 정답에 포함됐는지 확인 +- generateRandomNumbers() 랜덤한 숫자를 생성 +- createAnswer() 불변클래스의 팩토리 메서드 +- hasNum() 매개변수로 받는 숫자가 정답에 포함됐는지 확인 ### ComponentFactory - 컴포넌트들의 생성을 담당 -- (baseballController) baseballController 생성 -- (baseballService) baseballService 생성 -- (baseballRepository) baseballRepository 생성 -- (inputValidator) inputValidator 생성 +- 각 컴포넌트들을 생성 ### InputValidator - 유저의 시도에 대한 입력값 검증 -- (validateUserAttempt) 유저의 시도에 대한 입력값 검증 -- (notValidAttemptString) 입력 String 이 유효한지 검증 -- (hasDuplicate) 중복된 숫자가 입력되었는지 검증 -- (validateIsContinue) 계속 진행할지 여부에 대한 입력 검증 -- (notValidContinueString) 계속 진행할지 여부에 대한 입력값 검증 +- validateUserAttempt() 유저의 시도에 대한 입력값 검증 +- notValidAttemptString() 입력 String 이 유효한지 검증 +- hasDuplicate() 중복된 숫자가 입력되었는지 검증 +- validateIsContinue() 계속 진행할지 여부에 대한 입력 검증 +- notValidContinueString() 계속 진행할지 여부에 대한 입력값 검증 -### CountProvider - 스트림에서 스트라이크, 볼의 개수를 세는 함수형 인터페이스 -- (countOf) Stream 를 받아 Integer 를 반환 +### InputReader - 사용자의 입력값 받아와 검증 +- readUserAttempt() 유저의 시도 입력 받기 및 입력값 검증 +- readIsContinue() 게임 이어서 하기 여부 입력 받기 및 입력값 검증 + +### OutputWriter - 문자열 출력 +- write() 문자열을 그대로 출력 +- writeWithLine() 맨 끝에 개행문자와 함께 출력 ### ResultProvider - Answer 객체와 유저의 입력을 받아 결과를 반환 -- (getResult) 최종 출력할 결과를 반환 -- (countOfStrikeBall) 스트라이크, 볼의 개수를 계산 -- (getStrikeBallStream) 입력값을 StrikeBall 으로 변환한 스트림을 반환 -- (checkStrikeBall) 입력값 한 자리를 인덱스와 함께 비교해 strike, ball 여부를 판단 -- (checkBallOrNone) 같은 인덱스에 같은 값이 없는 경우 ball 인지 판단 -- (findAnswerNumByIndex) 정답 숫자에서 인덱스에 해당하는 숫자 반환 -- (convertToIntWithIndex) String 에서 인덱스에 해당하는 숫자를 int 로 변환 +- calculateResult() 최종 출력할 결과를 반환 +- mapToStrikeBalls() StrikeBall의 리스트로 변환 +- checkStrikeBall() 계산을 통해 StrikeBall 하나로 변환 및 반환 +- checkBallOrNone() 같은 인덱스에 같은 값이 없는 경우 ball 인지 판단 +- findAnswerNumByIndex() 정답 숫자에서 인덱스에 해당하는 숫자 반환 +- convertToIntWithIndex() String 에서 인덱스에 해당하는 숫자를 int 로 변환 + +### ResultOutputFormatter - 출력할 결과 포맷을 만듦 +- writeWithLine() 개행문자화 함께 출력 +- write() 문자열 그대로 출력 +- writeWithFormat() 결과 형태로 변환하여 출력 + +### Result - strike 개수롸 ball 개수를 갖는 dto +- getStrikeNum() strike 개수 반환 +- getBallNum() ball 개수 반환 + +### ResultOutputFormatter - Result를 결과 문자열로 변환 +- format() 결과 문자열로 변환 +- getFormattedOutput() format 진행 +- toCountResult() strike 혹은 ball 이 있을 경우 변환 진행 +- appendStrikeIfExists() strike의 개수에 따라 분기하여 변환 ## 구현 열거형 명세 @@ -76,6 +90,5 @@ ### StrikeBall - 스트라이크, 볼을 나타낸다. -### BallCount -- 스트라이크, 볼의 개수에 따른 볼카운트를 계산한다. -- 상황에 맞는 메시지를 갖는다. +### ResultMessage +- 결과 메시지를 나타낸다. diff --git a/src/main/java/baseball/Answer.java b/src/main/java/baseball/Answer.java index 73d45af494..7f5381a539 100644 --- a/src/main/java/baseball/Answer.java +++ b/src/main/java/baseball/Answer.java @@ -12,6 +12,10 @@ private Answer() { this.currentAnswer = Collections.unmodifiableList(generateRandomNumbers()); } + public static Answer createAnswer() { + return new Answer(); + } + private List generateRandomNumbers() { return Randoms.pickUniqueNumbersInRange( RandomNumberRange.MIN.getNum(), @@ -23,10 +27,6 @@ public Integer findByIndex(final Integer index) { return currentAnswer.get(index); } - public static Answer createAnswer() { - return new Answer(); - } - public boolean hasNum(final int attemptNum) { return currentAnswer.contains(attemptNum); } diff --git a/src/main/java/baseball/BallCount.java b/src/main/java/baseball/BallCount.java deleted file mode 100644 index 33280f7f55..0000000000 --- a/src/main/java/baseball/BallCount.java +++ /dev/null @@ -1,37 +0,0 @@ -package baseball; - -import java.util.Arrays; -import java.util.function.BiPredicate; - -public enum BallCount { - THREE_STRIKE((s, b) -> s == 3, "3스트라이크"), - TWO_STRIKE((s, b) -> b == 0 && s == 2 , "2스트라이크"), - ONE_BALL_TWO_STRIKE((s, b) -> b == 1 && s == 2, "1볼 2스트라이크"), - ONE_BALL_ONE_STRIKE((s, b) -> b == 1 && s == 1, "1볼 1스트라이크"), - TWO_BALL_ONE_STRIKE((s, b) -> b == 2 && s == 1, "2볼 1스트라이크"), - ONE_STRIKE((s, b) -> b == 0 && s == 1 , "1스트라이크"), - THREE_BALL((s, b) -> b == 3 && s == 0, "3볼"), - TWO_BALL((s, b) -> b == 2 && s == 0, "2볼"), - ONE_BALL((s, b) -> b == 1 && s == 0, "1볼"), - NOTHING((s, b) -> b == 0 && s == 0, "낫싱"); - - private final BiPredicate strikeBallNumMatcher; - private final String message; - - BallCount(final BiPredicate strikeBallNumMatcher, final String message) { - this.strikeBallNumMatcher = strikeBallNumMatcher; - this.message = message; - } - - public static BallCount ofStrikeAndBallNum(final Integer ballNum, final Integer strikeNum) { - return Arrays.stream(BallCount.values()) - .filter(ballCount -> ballCount.strikeBallNumMatcher.test(strikeNum, ballNum)) - .findFirst() - .orElseThrow(IllegalArgumentException::new); - } - - @Override - public String toString() { - return this.message; - } -} diff --git a/src/main/java/baseball/BaseballController.java b/src/main/java/baseball/BaseballController.java index 9cb91c4335..9e9e49a4dc 100644 --- a/src/main/java/baseball/BaseballController.java +++ b/src/main/java/baseball/BaseballController.java @@ -2,27 +2,25 @@ public class BaseballController { private static final int CONTINUE_COMMAND = 1; + private static final int ATTEMPT_SUCCESS = 3; private final BaseballService baseballService; - private final InputValidator inputValidator; private final OutputWriter outputWriter; private final InputReader inputReader; public BaseballController( final BaseballService baseballService, - final InputValidator inputValidator, final OutputWriter outputWriter, final InputReader inputReader) { this.baseballService = baseballService; - this.inputValidator = inputValidator; this.outputWriter = outputWriter; this.inputReader = inputReader; } public void startGame() { boolean isContinue = true; - while(isContinue) { - outputWriter.printWithLine(GameMessage.GAME_START); + while (isContinue) { + outputWriter.writeWithLine(GameMessage.GAME_START); baseballService.saveAnswer(); playGame(); isContinue = askIsContinue(); @@ -30,39 +28,32 @@ public void startGame() { } private boolean askIsContinue() { - outputWriter.printWithLine(GameMessage.CREATE_NEW_GAME_OR_NOT); - final String input = inputReader.read(); - inputValidator.validateIsContinue(input); + outputWriter.writeWithLine(GameMessage.CREATE_NEW_GAME_OR_NOT); + final String input = inputReader.readIsContinue(); return Integer.parseInt(input) == CONTINUE_COMMAND; } private void playGame() { boolean isContinue = true; - while(isContinue) { + while (isContinue) { final String userAttempt = getUserAttempt(); - final BallCount result = getResult(userAttempt); + final Result result = getResult(userAttempt); - if (result == BallCount.THREE_STRIKE) { - outputWriter.printWithLine(GameMessage.GAME_FINISH); + if (result.getStrikeNum() == ATTEMPT_SUCCESS) { + outputWriter.writeWithLine(GameMessage.GAME_FINISH); isContinue = false; } } } private String getUserAttempt() { - outputWriter.print(GameMessage.INPUT_NEXT_NUMBER); - final String userAttempt = inputReader.read(); - validateUserAttempt(userAttempt); - return userAttempt; + outputWriter.write(GameMessage.INPUT_NEXT_NUMBER); + return inputReader.readUserAttempt(); } - private BallCount getResult(final String userAttempt) { - final BallCount result = baseballService.calculateResult(userAttempt); - outputWriter.printWithLine(result); + private Result getResult(final String userAttempt) { + final Result result = baseballService.calculateResult(userAttempt); + outputWriter.writeWithFormat(result); return result; } - - private void validateUserAttempt(final String userAttempt) { - inputValidator.validateUserAttempt(userAttempt); - } } diff --git a/src/main/java/baseball/BaseballRepository.java b/src/main/java/baseball/BaseballRepository.java index 99cade22e3..efad2ae4ed 100644 --- a/src/main/java/baseball/BaseballRepository.java +++ b/src/main/java/baseball/BaseballRepository.java @@ -2,11 +2,12 @@ public class BaseballRepository { private Answer answer; + public void saveAnswer(final Answer answer) { this.answer = answer; } public Answer findCurrentAnswer() { - return answer; + return this.answer; } } diff --git a/src/main/java/baseball/BaseballService.java b/src/main/java/baseball/BaseballService.java index 540c37fbd1..e0dbf36b5c 100644 --- a/src/main/java/baseball/BaseballService.java +++ b/src/main/java/baseball/BaseballService.java @@ -12,10 +12,10 @@ public void saveAnswer() { baseballRepository.saveAnswer(answer); } - public BallCount calculateResult(final String attempt) { + public Result calculateResult(final String attempt) { final Answer currentAnswer = findCurrentAnswer(); final ResultProvider resultProvider = new ResultProvider(currentAnswer, attempt); - return resultProvider.getResult(); + return resultProvider.calculateResult(); } private Answer findCurrentAnswer() { diff --git a/src/main/java/baseball/ComponentFactory.java b/src/main/java/baseball/ComponentFactory.java index 39b9ffa444..e2760e2ed0 100644 --- a/src/main/java/baseball/ComponentFactory.java +++ b/src/main/java/baseball/ComponentFactory.java @@ -5,17 +5,20 @@ public class ComponentFactory { public BaseballController baseballController() { return new BaseballController( baseballService(), - userAttemptValidator(), outputWriter(), inputReader()); } + private ResultOutputFormatter resultOutputFormatter() { + return new ResultOutputFormatter(); + } + private InputReader inputReader() { - return new InputReader(); + return new InputReader(inputValidator()); } private OutputWriter outputWriter() { - return new OutputWriter(); + return new OutputWriter(resultOutputFormatter()); } private BaseballService baseballService() { @@ -26,7 +29,7 @@ private BaseballRepository baseballRepository() { return new BaseballRepository(); } - private InputValidator userAttemptValidator() { + private InputValidator inputValidator() { return new InputValidator(); } } diff --git a/src/main/java/baseball/CountProvider.java b/src/main/java/baseball/CountProvider.java deleted file mode 100644 index 77fdf6f510..0000000000 --- a/src/main/java/baseball/CountProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package baseball; - -import java.util.stream.Stream; - -@FunctionalInterface -public interface CountProvider { - Integer countOf(Stream strikeBall); -} diff --git a/src/main/java/baseball/GameMessage.java b/src/main/java/baseball/GameMessage.java index e875483839..7d257525c6 100644 --- a/src/main/java/baseball/GameMessage.java +++ b/src/main/java/baseball/GameMessage.java @@ -4,8 +4,7 @@ public enum GameMessage { GAME_START("숫자 야구 게임을 시작합니다."), INPUT_NEXT_NUMBER("숫자를 입력해주세요 : "), GAME_FINISH("3개의 숫자를 모두 맞히셨습니다! 게임 종료"), - CREATE_NEW_GAME_OR_NOT("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.") - ; + CREATE_NEW_GAME_OR_NOT("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요."); private final String message; GameMessage(final String message) { diff --git a/src/main/java/baseball/InputReader.java b/src/main/java/baseball/InputReader.java index 8216315714..b18103ca85 100644 --- a/src/main/java/baseball/InputReader.java +++ b/src/main/java/baseball/InputReader.java @@ -3,7 +3,21 @@ import camp.nextstep.edu.missionutils.Console; public class InputReader { - public String read() { - return Console.readLine(); + private final InputValidator inputValidator; + + public InputReader(final InputValidator inputValidator) { + this.inputValidator = inputValidator; + } + + public String readUserAttempt() { + final String input = Console.readLine(); + inputValidator.validateUserAttempt(input); + return input; + } + + public String readIsContinue() { + final String input = Console.readLine(); + inputValidator.validateIsContinue(input); + return input; } } diff --git a/src/main/java/baseball/InputValidator.java b/src/main/java/baseball/InputValidator.java index 5d05d24b32..3e8364aa52 100644 --- a/src/main/java/baseball/InputValidator.java +++ b/src/main/java/baseball/InputValidator.java @@ -4,7 +4,7 @@ public class InputValidator { private static final String NUMERIC_MATCHER = "-?\\d+"; private static final Integer IS_CONTINUE_LENGTH = 1; private static final Integer CONTINUE = 1; - private static final Integer NOT_CONTINUE = 1; + private static final Integer NOT_CONTINUE = 2; public void validateUserAttempt(final String userAttempt) { diff --git a/src/main/java/baseball/OutputWriter.java b/src/main/java/baseball/OutputWriter.java index 0b7058ac91..ecf75ca105 100644 --- a/src/main/java/baseball/OutputWriter.java +++ b/src/main/java/baseball/OutputWriter.java @@ -1,15 +1,21 @@ package baseball; public class OutputWriter { - public void printWithLine(final GameMessage gameMessage) { + private final ResultOutputFormatter resultOutputFormatter; + + public OutputWriter(final ResultOutputFormatter resultOutputFormatter) { + this.resultOutputFormatter = resultOutputFormatter; + } + + public void writeWithLine(final GameMessage gameMessage) { System.out.println(gameMessage); } - public void print(final GameMessage gameMessage) { + public void write(final GameMessage gameMessage) { System.out.print(gameMessage); } - public void printWithLine(final BallCount ballCount) { - System.out.println(ballCount); + public void writeWithFormat(final Result result) { + System.out.println(resultOutputFormatter.format(result)); } } diff --git a/src/main/java/baseball/Result.java b/src/main/java/baseball/Result.java new file mode 100644 index 0000000000..3e1562fc37 --- /dev/null +++ b/src/main/java/baseball/Result.java @@ -0,0 +1,19 @@ +package baseball; + +public class Result { + private final int strikeNum; + private final int ballNum; + + public Result(final int strikeNum, final int ballNum) { + this.strikeNum = strikeNum; + this.ballNum = ballNum; + } + + public int getStrikeNum() { + return this.strikeNum; + } + + public int getBallNum() { + return this.ballNum; + } +} diff --git a/src/main/java/baseball/ResultMessage.java b/src/main/java/baseball/ResultMessage.java new file mode 100644 index 0000000000..63e55e3c76 --- /dev/null +++ b/src/main/java/baseball/ResultMessage.java @@ -0,0 +1,18 @@ +package baseball; + +public enum ResultMessage { + BALL("%d볼"), + STRIKE("%d스트라이크"), + NOTHING("낫싱"); + + private final String message; + + ResultMessage(final String message) { + this.message = message; + } + + @Override + public String toString() { + return this.message; + } +} diff --git a/src/main/java/baseball/ResultOutputFormatter.java b/src/main/java/baseball/ResultOutputFormatter.java new file mode 100644 index 0000000000..b004e5f893 --- /dev/null +++ b/src/main/java/baseball/ResultOutputFormatter.java @@ -0,0 +1,38 @@ +package baseball; + +public class ResultOutputFormatter { + private static final int NONE = 0; + private static final String BLANK = " "; + + public String format(final Result result) { + final int ballNum = result.getBallNum(); + final int strikeNum = result.getStrikeNum(); + + return getFormattedOutput(ballNum, strikeNum); + } + + private String getFormattedOutput(final int ballNum, final int strikeNum) { + if (ballNum == NONE && strikeNum == NONE) { + return ResultMessage.NOTHING.toString(); + } + return toCountResult(ballNum, strikeNum); + } + + private String toCountResult(final int ballNum, final int strikeNum) { + final StringBuilder stringBuilder = new StringBuilder(); + if (ballNum != NONE) { + stringBuilder + .append(String.format(ResultMessage.BALL.toString(), ballNum)) + .append(BLANK); + } + return appendStrikeIfExists(stringBuilder, strikeNum); + } + + private String appendStrikeIfExists(final StringBuilder stringBuilder, final int strikeNum) { + if (strikeNum != NONE) { + stringBuilder + .append(String.format(ResultMessage.STRIKE.toString(), strikeNum)); + } + return stringBuilder.toString(); + } +} diff --git a/src/main/java/baseball/ResultProvider.java b/src/main/java/baseball/ResultProvider.java index f5df0ae837..a0ae747caf 100644 --- a/src/main/java/baseball/ResultProvider.java +++ b/src/main/java/baseball/ResultProvider.java @@ -1,11 +1,11 @@ package baseball; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import java.util.stream.IntStream; -import java.util.stream.Stream; public class ResultProvider { - private final CountProvider strikeCountProvider = s -> Long.valueOf(s.filter(i -> i == StrikeBall.STRIKE).count()).intValue(); - private final CountProvider ballCountProvider = s -> Long.valueOf(s.filter(i -> i == StrikeBall.BALL).count()).intValue(); private final Answer answer; private final String attempt; @@ -14,22 +14,19 @@ public ResultProvider(final Answer answer, final String attempt) { this.attempt = attempt; } - public BallCount getResult() { - final Integer strikeNum = countOfStrikeBall(strikeCountProvider, getStrikeBallStream()); - final Integer ballNum = countOfStrikeBall(ballCountProvider, getStrikeBallStream()); - return BallCount.ofStrikeAndBallNum(ballNum, strikeNum); + public Result calculateResult() { + final List strikeBalls = mapToStrikeBalls(); + final int strikeCount = Collections.frequency(strikeBalls, StrikeBall.STRIKE); + final int ballCount = Collections.frequency(strikeBalls, StrikeBall.BALL); + return new Result(strikeCount, ballCount); } - private Integer countOfStrikeBall(final CountProvider countProvider, final Stream strikeBallStream) { - return countProvider.countOf(strikeBallStream); - } - - private Stream getStrikeBallStream() { + private List mapToStrikeBalls() { return IntStream.range(0, 3).mapToObj(i -> { final int attemptNum = convertToIntWithIndex(i); final int answerNum = findAnswerNumByIndex(i); return checkStrikeBall(answerNum, attemptNum, i); - }); + }).collect(Collectors.toUnmodifiableList()); } private StrikeBall checkStrikeBall(final int answerNum, final int attemptNum, final int index) { diff --git a/src/test/java/baseball/BaseballServiceTest.java b/src/test/java/baseball/BaseballServiceTest.java index 83dcf5f26d..4233fcb6d7 100644 --- a/src/test/java/baseball/BaseballServiceTest.java +++ b/src/test/java/baseball/BaseballServiceTest.java @@ -39,10 +39,10 @@ void threeStrike() { final Integer third = currentAnswer.findByIndex(2); //when - final BallCount ballCount = baseballService.calculateResult(Integer.toString(first) + Integer.toString(second) + Integer.toString(third)); +// final BallCount ballCount = baseballService.calculateResult(Integer.toString(first) + Integer.toString(second) + Integer.toString(third)); //then - assertThat(ballCount).isEqualTo(BallCount.THREE_STRIKE); +// assertThat(ballCount).isEqualTo(BallCount.THREE_STRIKE); } @Test @@ -56,10 +56,10 @@ void threeBall() { final Integer third = currentAnswer.findByIndex(2); //when - final BallCount ballCount = baseballService.calculateResult(Integer.toString(third) + Integer.toString(first) + Integer.toString(second)); +// final BallCount ballCount = baseballService.calculateResult(Integer.toString(third) + Integer.toString(first) + Integer.toString(second)); //then - assertThat(ballCount).isEqualTo(BallCount.THREE_BALL); +// assertThat(ballCount).isEqualTo(BallCount.THREE_BALL); } @Test @@ -73,10 +73,10 @@ void oneStrikeTwoBall() { final Integer third = currentAnswer.findByIndex(2); //when - final BallCount ballCount = baseballService.calculateResult(Integer.toString(first) + Integer.toString(third) + Integer.toString(second)); +// final BallCount ballCount = baseballService.calculateResult(Integer.toString(first) + Integer.toString(third) + Integer.toString(second)); //then - assertThat(ballCount).isEqualTo(BallCount.TWO_BALL_ONE_STRIKE); +// assertThat(ballCount).isEqualTo(BallCount.TWO_BALL_ONE_STRIKE); } } } diff --git a/src/test/java/baseball/ResultProviderTest.java b/src/test/java/baseball/ResultProviderTest.java index 7a7dd34b13..816009699c 100644 --- a/src/test/java/baseball/ResultProviderTest.java +++ b/src/test/java/baseball/ResultProviderTest.java @@ -29,10 +29,10 @@ void threeStrikeResult() { Integer.toString(currentAnswer.findByIndex(2))); //when - final BallCount result = resultProvider.getResult(); +// final BallCount result = resultProvider.calculateResult(); //then - assertThat(result).isEqualTo(BallCount.THREE_STRIKE); +// assertThat(result).isEqualTo(BallCount.THREE_STRIKE); } @Test @@ -51,10 +51,10 @@ void threeBallResult() { Integer.toString(currentAnswer.findByIndex(1))); //when - final BallCount result = resultProvider.getResult(); +// final BallCount result = resultProvider.calculateResult(); //then - assertThat(result).isEqualTo(BallCount.THREE_BALL); +// assertThat(result).isEqualTo(BallCount.THREE_BALL); } @Test @@ -73,10 +73,10 @@ void twoBallOneStrikeResult() { Integer.toString(currentAnswer.findByIndex(1))); //when - final BallCount result = resultProvider.getResult(); +// final BallCount result = resultProvider.calculateResult(); //then - assertThat(result).isEqualTo(BallCount.TWO_BALL_ONE_STRIKE); +// assertThat(result).isEqualTo(BallCount.TWO_BALL_ONE_STRIKE); } } } From a323cd4b16434bf4c3e0d0c8242af0d898f989bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Thu, 3 Aug 2023 19:46:47 +0900 Subject: [PATCH 11/12] refactor: change random generation method --- src/main/java/baseball/Answer.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/baseball/Answer.java b/src/main/java/baseball/Answer.java index 7f5381a539..5dcbbe8ff6 100644 --- a/src/main/java/baseball/Answer.java +++ b/src/main/java/baseball/Answer.java @@ -2,6 +2,7 @@ import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -17,10 +18,14 @@ public static Answer createAnswer() { } private List generateRandomNumbers() { - return Randoms.pickUniqueNumbersInRange( - RandomNumberRange.MIN.getNum(), - RandomNumberRange.MAX.getNum(), - RandomNumberRange.CIPHER.getNum()); + List computer = new ArrayList<>(); + while (computer.size() < 3) { + int randomNumber = Randoms.pickNumberInRange(1, 9); + if (!computer.contains(randomNumber)) { + computer.add(randomNumber); + } + } + return computer; } public Integer findByIndex(final Integer index) { From ef80c75af058558b262feef3431741073bfb63c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=8A=B9=EC=A7=84?= Date: Thu, 3 Aug 2023 19:53:06 +0900 Subject: [PATCH 12/12] refactor: rename variable --- src/main/java/baseball/Answer.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/baseball/Answer.java b/src/main/java/baseball/Answer.java index 5dcbbe8ff6..4328a272d9 100644 --- a/src/main/java/baseball/Answer.java +++ b/src/main/java/baseball/Answer.java @@ -18,14 +18,14 @@ public static Answer createAnswer() { } private List generateRandomNumbers() { - List computer = new ArrayList<>(); - while (computer.size() < 3) { + List randomNumbers = new ArrayList<>(); + while (randomNumbers.size() < 3) { int randomNumber = Randoms.pickNumberInRange(1, 9); - if (!computer.contains(randomNumber)) { - computer.add(randomNumber); + if (!randomNumbers.contains(randomNumber)) { + randomNumbers.add(randomNumber); } } - return computer; + return randomNumbers; } public Integer findByIndex(final Integer index) {