From c6c006b7d351fa6ab9e70fe62ea8bee8d0cc348c Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Mon, 27 Nov 2023 13:12:53 +0900 Subject: [PATCH 01/20] =?UTF-8?q?:memo:=20[Docs]=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=ED=95=A0=20=EA=B8=B0=EB=8A=A5=20=EB=AA=A9=EB=A1=9D=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/docs/README.md b/docs/README.md index e69de29bb2d..9dc33e57d0a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -0,0 +1,95 @@ +## ✏️ 구현할 기능 목록 + +___ + +## 입력 + +- [ ] `로또 구입 금액` 입력 + + - 비기능적 요구사항 + - null 값이거나 빈 값인지 확인한다. + - 구입 금액은 정수만 가능하다. + - 구입 금액은 1이상의 자연수이다. + + - 기능적 요구사항 + - 구입 금액은 `1,000원 단위`이다. + + +- [ ] `당첨 번호` 입력 (번호는 쉼표(,)를 기준으로 구분한다.) + + - 비기능적 요구사항 + ``` + 1,2,3,4,5,6 + ``` + - null 값이거나 빈 값인지 확인한다. + - 당첨 번호는 정수만 가능하다. + - 당첨 번호은 1이상의 자연수이다. + - `,,` 와 같은 잘못된 입력 값 검증 + - 가장 앞과 가장 뒤 입력 값은 숫자여야 한다. + + - 기능적 요구사항 + - 중복된 숫자는 입력 불가하다. + - 1 ~ 45 사이 숫자만 입력 가능하다. + +- [ ] `보너스 번호` 입력 + + - 비기능적 요구사항 + - null 값이거나 빈 값인지 확인한다. + - 보너스 번호는 정수만 가능하다. + - 보너스 번호은 1이상의 자연수이다. + + - 기능적 요구사항 + - 1 ~ 45 사이 숫자만 입력 가능하다. + - 당첨 번호와 중복 불가하다. + +## 로또 번호 + +- [ ] 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. + - 로또 번호는 랜덤이다. + +## 당첨 통계 + +- [ ] 당첨 통계는 다음과 같다. + + ``` + 3개 일치 (5,000원) - 1개 + 4개 일치 (50,000원) - 0개 + 5개 일치 (1,500,000원) - 0개 + 5개 일치, 보너스 볼 일치 (30,000,000원) - 0개 + 6개 일치 (2,000,000,000원) - 0개 + 총 수익률은 62.5%입니다. + + ``` + +- [ ] 수익률 계산 + - 수익률은 소수점 둘째 자리에서 반올림한다. + +## 출력 + +- [ ] `구입금액을 입력해 주세요.` 출력 + +- [ ] `?개를 구매했습니다.` 출력 + - 로또 번호 출력 (랜덤) + +- [ ] `당첨 번호를 입력해 주세요.` 출력 + +- [ ] `보너스 번호를 입력해 주세요.` 출력 + +- [ ] `당첨 통계` 출력 + ``` + --- + 3개 일치 (5,000원) - 1개 + 4개 일치 (50,000원) - 0개 + 5개 일치 (1,500,000원) - 0개 + 5개 일치, 보너스 볼 일치 (30,000,000원) - 0개 + 6개 일치 (2,000,000,000원) - 0개 + 총 수익률은 62.5%입니다. + + ``` + +--- + +## 공통 검증 + +- [ ] 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. +- [ ] 1,000단위마다 콤마를 찍기 \ No newline at end of file From ecd062b29e3c0dded50ae092ef27e74967ee92b1 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:12:50 +0900 Subject: [PATCH 02/20] :sparkles: [Feat] InputView & Validation --- .../lotto/exception/ErrorInputException.java | 45 +++++++++++++++ src/main/java/lotto/view/input/InputView.java | 55 +++++++++++++++++++ .../template/InputValidatorCallback.java | 5 ++ .../template/InputValidatorTemplate.java | 16 ++++++ 4 files changed, 121 insertions(+) create mode 100644 src/main/java/lotto/exception/ErrorInputException.java create mode 100644 src/main/java/lotto/view/input/InputView.java create mode 100644 src/main/java/lotto/view/input/template/InputValidatorCallback.java create mode 100644 src/main/java/lotto/view/input/template/InputValidatorTemplate.java diff --git a/src/main/java/lotto/exception/ErrorInputException.java b/src/main/java/lotto/exception/ErrorInputException.java new file mode 100644 index 00000000000..d9d54343392 --- /dev/null +++ b/src/main/java/lotto/exception/ErrorInputException.java @@ -0,0 +1,45 @@ +package lotto.exception; + +public class ErrorInputException extends IllegalArgumentException { + public enum ErrorMessage { + PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY("[ERROR] 구입 금액은 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), + PURCHASE_PRICE_IS_NOT_INTEGER("[ERROR] 구입 금액은 정수만 입력 가능합니다. 다시 입력해 주세요."), + PURCHASE_PRICE_IS_MORE_THAN_ONE("[ERROR] 구입 금액은 1이상의 자연수여야 합니다. 다시 입력해 주세요."), + PURCHASE_PRICE_CAN_DIVIDE_BY_THOUSAND("[ERROR] 구입 금액은 1,000원 단위여야 합니다.다시 입력해 주세요."), + + LOTTO_NUMBER_CAN_NOT_BE_DUPLICATE("[ERROR] 로또 번호는 중복된 숫자로 구성되면 안됩니다. 다시 입력해 주세요."), + + WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY("[ERROR] 당첨 번호는 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), + WINNER_NUMBER_IS_NOT_INTEGER("[ERROR] 당첨 번호는 정수만 입력 가능합니다. 다시 입력해 주세요."), + WINNER_NUMBER_IS_MORE_THAN_ONE("[ERROR] 당첨 번호는 1이상의 자연수여야 합니다. 다시 입력해 주세요."), + WINNER_NUMBER_ERROR_INPUT("[ERROR] 유효하지 않은 입력 입니다. 다시 입력해 주세요."), + WINNER_NUMBER_SIZE_IS_SIX("[ERROR] 당첨 번호는 6개로 구성되어야 합니다. 다시 입력해 주세요."), + WINNER_NUMBER_CAN_NOT_BE_DUPLICATE("[ERROR] 당첨 번호는 중복된 숫자로 구성되면 안됩니다. 다시 입력해 주세요."), + WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE("[ERROR] 당첨 번호는 1 ~ 45 사이 숫자여야 합니다. 다시 입력해 주세요."), + + + BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY("[ERROR] 보너스 번호는 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), + BONUS_NUMBER_IS_NOT_INTEGER("[ERROR] 보너스 번호는 정수만 입력 가능합니다. 다시 입력해 주세요."), + BONUS_NUMBER_IS_MORE_THAN_ONE("[ERROR] 보너스 번호는 1이상의 자연수여야 합니다. 다시 입력해 주세요."), + BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE("[ERROR] 보너스 번호는 1 ~ 45 사이 숫자여야 합니다. 다시 입력해 주세요."), + BONUS_NUMBER_CAN_NOT_BE_DUPLICATE("[ERROR] 보너스 번호는 당첨 번호와 중복된 숫자이면 안됩니다. 다시 입력해 주세요."), + + CAN_NOT_STATISTIC("[ERROR] 당첨 조회가 불가능한 조긴입니다."); + + + ErrorMessage(final String message) { + this.message = message; + } + + private final String message; + + public String getMessage() { + return message; + } + + } + + public ErrorInputException(ErrorMessage message) { + super(message.getMessage()); + } +} diff --git a/src/main/java/lotto/view/input/InputView.java b/src/main/java/lotto/view/input/InputView.java new file mode 100644 index 00000000000..4ccdd970d1b --- /dev/null +++ b/src/main/java/lotto/view/input/InputView.java @@ -0,0 +1,55 @@ +package lotto.view.input; + +import java.util.List; +import lotto.validation.BonusNumberValidator; +import lotto.validation.PurchasePriceValidator; +import lotto.validation.WinnerNumberValidator; +import lotto.view.input.template.InputValidatorTemplate; + +public class InputView { + private final PurchasePriceValidator purchasePriceValidator; + private final WinnerNumberValidator winnerNumberValidator; + private final BonusNumberValidator bonusNumberValidator; + private final InputValidatorTemplate template; + + public InputView(PurchasePriceValidator purchasePriceValidator, WinnerNumberValidator winnerNumberValidator, + BonusNumberValidator bonusNumberValidator, InputValidatorTemplate template) { + this.purchasePriceValidator = purchasePriceValidator; + this.winnerNumberValidator = winnerNumberValidator; + this.bonusNumberValidator = bonusNumberValidator; + this.template = template; + } + + + public int readPrice() { + return template.InputWithValidation(this::purchasePriceValidate); + } + + public List inputWinnerNumber() { + return template.InputWithValidation(this::WinnerNumberValidate); + } + + public int inputBonusNumber() { + return template.InputWithValidation(this::BonusNumberValidate); + } + + private int purchasePriceValidate(String input) { + purchasePriceValidator.isNullOrEmpty(input); + int purchasePrice = purchasePriceValidator.isInteger(input); + return purchasePriceValidator.moreThanOne(purchasePrice); + } + + + private List WinnerNumberValidate(String input) { + winnerNumberValidator.isNullOrEmpty(input); + winnerNumberValidator.checkCommaError(input); + List winnerNumber = winnerNumberValidator.isInteger(input); + return winnerNumberValidator.moreThanOne(winnerNumber); + } + + private int BonusNumberValidate(String input) { + bonusNumberValidator.isNullOrEmpty(input); + int bonusNumber = bonusNumberValidator.isInteger(input); + return bonusNumberValidator.moreThanOne(bonusNumber); + } +} diff --git a/src/main/java/lotto/view/input/template/InputValidatorCallback.java b/src/main/java/lotto/view/input/template/InputValidatorCallback.java new file mode 100644 index 00000000000..a0dde238318 --- /dev/null +++ b/src/main/java/lotto/view/input/template/InputValidatorCallback.java @@ -0,0 +1,5 @@ +package lotto.view.input.template; + +public interface InputValidatorCallback { + T validate(String input); +} \ No newline at end of file diff --git a/src/main/java/lotto/view/input/template/InputValidatorTemplate.java b/src/main/java/lotto/view/input/template/InputValidatorTemplate.java new file mode 100644 index 00000000000..3827a7b4af8 --- /dev/null +++ b/src/main/java/lotto/view/input/template/InputValidatorTemplate.java @@ -0,0 +1,16 @@ +package lotto.view.input.template; + +import static camp.nextstep.edu.missionutils.Console.readLine; + +public class InputValidatorTemplate { + public T InputWithValidation(InputValidatorCallback validator) { + while (true) { + try { + String input = readLine(); + return validator.validate(input); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } +} \ No newline at end of file From dfcc569f4904a0fdc6d143d32a5ceeedc269b635 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:15:32 +0900 Subject: [PATCH 03/20] :sparkles: [Feat] model --- src/main/java/lotto/Lotto.java | 20 ------- src/main/java/lotto/model/BonusNumber.java | 40 ++++++++++++++ src/main/java/lotto/model/Lotto.java | 43 +++++++++++++++ src/main/java/lotto/model/LottoNumbers.java | 20 +++++++ src/main/java/lotto/model/PurchasePrice.java | 34 ++++++++++++ src/main/java/lotto/model/Statistics.java | 56 ++++++++++++++++++++ src/main/java/lotto/model/WinnerNumber.java | 51 ++++++++++++++++++ 7 files changed, 244 insertions(+), 20 deletions(-) delete mode 100644 src/main/java/lotto/Lotto.java create mode 100644 src/main/java/lotto/model/BonusNumber.java create mode 100644 src/main/java/lotto/model/Lotto.java create mode 100644 src/main/java/lotto/model/LottoNumbers.java create mode 100644 src/main/java/lotto/model/PurchasePrice.java create mode 100644 src/main/java/lotto/model/Statistics.java create mode 100644 src/main/java/lotto/model/WinnerNumber.java diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java deleted file mode 100644 index 519793d1f73..00000000000 --- a/src/main/java/lotto/Lotto.java +++ /dev/null @@ -1,20 +0,0 @@ -package lotto; - -import java.util.List; - -public class Lotto { - private final List numbers; - - public Lotto(List numbers) { - validate(numbers); - this.numbers = numbers; - } - - private void validate(List numbers) { - if (numbers.size() != 6) { - throw new IllegalArgumentException(); - } - } - - // TODO: 추가 기능 구현 -} diff --git a/src/main/java/lotto/model/BonusNumber.java b/src/main/java/lotto/model/BonusNumber.java new file mode 100644 index 00000000000..26a0da8e037 --- /dev/null +++ b/src/main/java/lotto/model/BonusNumber.java @@ -0,0 +1,40 @@ +package lotto.model; + +import static lotto.constant.Constant.MAX_LOTTO_NUMBER; +import static lotto.constant.Constant.MIN_LOTTO_NUMBER; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_CAN_NOT_BE_DUPLICATE; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE; + +import java.util.List; +import lotto.exception.ErrorInputException; + +public class BonusNumber { + + private final int bonusNumber; + + private BonusNumber(List winnerNumber, int bonusNumber) { + this.bonusNumber = bonusNumber; + isDuplicate(winnerNumber); + isBetweenOneAndFortyFive(); + } + + public int getBonusNumber() { + return bonusNumber; + } + + public static BonusNumber createBonusNumber(List winnerNumber, int bonusNumber) { + return new BonusNumber(winnerNumber, bonusNumber); + } + + private void isDuplicate(List winnerNumbers) { + if (winnerNumbers.contains(bonusNumber)) { + throw new ErrorInputException(BONUS_NUMBER_CAN_NOT_BE_DUPLICATE); + } + } + + private void isBetweenOneAndFortyFive() { + if (bonusNumber < MIN_LOTTO_NUMBER || bonusNumber > MAX_LOTTO_NUMBER) { + throw new ErrorInputException(BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE); + } + } +} diff --git a/src/main/java/lotto/model/Lotto.java b/src/main/java/lotto/model/Lotto.java new file mode 100644 index 00000000000..41a7e23ee22 --- /dev/null +++ b/src/main/java/lotto/model/Lotto.java @@ -0,0 +1,43 @@ +package lotto.model; + +import static camp.nextstep.edu.missionutils.Randoms.pickUniqueNumbersInRange; +import static lotto.constant.Constant.LOTTO_SIZE; +import static lotto.constant.Constant.MAX_LOTTO_NUMBER; +import static lotto.constant.Constant.MIN_LOTTO_NUMBER; +import static lotto.exception.ErrorInputException.ErrorMessage.LOTTO_NUMBER_CAN_NOT_BE_DUPLICATE; + +import java.util.Collections; +import java.util.List; +import lotto.exception.ErrorInputException; + +public class Lotto { + private final List numbers; + + public Lotto(List numbers) { + validate(numbers); + isDuplicate(numbers); + this.numbers = numbers; + } + + public static Lotto createLotto() { + List pickedNumbers = pickUniqueNumbersInRange(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER, LOTTO_SIZE); + return new Lotto(pickedNumbers); + } + + public List getNumbers() { + return Collections.unmodifiableList(numbers); + } + + private void validate(List numbers) { + if (numbers.size() != LOTTO_SIZE) { + throw new IllegalArgumentException(); + } + } + + private void isDuplicate(List numbers) { + long countDistinct = numbers.stream().distinct().count(); + if (countDistinct < numbers.size()) { + throw new ErrorInputException(LOTTO_NUMBER_CAN_NOT_BE_DUPLICATE); + } + } +} diff --git a/src/main/java/lotto/model/LottoNumbers.java b/src/main/java/lotto/model/LottoNumbers.java new file mode 100644 index 00000000000..acc45cd51ca --- /dev/null +++ b/src/main/java/lotto/model/LottoNumbers.java @@ -0,0 +1,20 @@ +package lotto.model; + +import java.util.Collections; +import java.util.List; + +public class LottoNumbers { + private final List numbers; + + private LottoNumbers(List numbers) { + this.numbers = numbers; + } + + public static LottoNumbers createLottoNumbers(List numbers) { + return new LottoNumbers(numbers); + } + + public List getNumbers() { + return Collections.unmodifiableList(numbers); + } +} diff --git a/src/main/java/lotto/model/PurchasePrice.java b/src/main/java/lotto/model/PurchasePrice.java new file mode 100644 index 00000000000..e198ce9ee52 --- /dev/null +++ b/src/main/java/lotto/model/PurchasePrice.java @@ -0,0 +1,34 @@ +package lotto.model; + +import static lotto.constant.Constant.THOUSAND; +import static lotto.constant.Constant.ZERO; +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_CAN_DIVIDE_BY_THOUSAND; + +import lotto.exception.ErrorInputException; + +public class PurchasePrice { + private final int price; + + private PurchasePrice(int price) { + isDividedByThousand(); + this.price = price; + } + + public static PurchasePrice createPurchasePrice(int price) { + return new PurchasePrice(price); + } + + public int getPrice() { + return price; + } + + private void isDividedByThousand() { + if (price % THOUSAND != ZERO) { + throw new ErrorInputException(PURCHASE_PRICE_CAN_DIVIDE_BY_THOUSAND); + } + } + + public int calculateLottoCount() { + return price / THOUSAND; + } +} diff --git a/src/main/java/lotto/model/Statistics.java b/src/main/java/lotto/model/Statistics.java new file mode 100644 index 00000000000..7010f04e689 --- /dev/null +++ b/src/main/java/lotto/model/Statistics.java @@ -0,0 +1,56 @@ +package lotto.model; + +import static lotto.constant.Constant.ZERO; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import lotto.constant.Rank; + +public class Statistics { + private final Map result; + + private Statistics() { + result = new EnumMap<>(Rank.class); + for (Rank rank : Rank.values()) { + result.put(rank, ZERO); + } + } + + public static Statistics createStatistics() { + return new Statistics(); + } + + public Map getResult() { + return result; + } + + public void calculateMatching(LottoNumbers lottoNumbers, WinnerNumber winnerNumber, BonusNumber bonusNumber) { + List lottos = lottoNumbers.getNumbers(); + + for (Lotto lotto : lottos) { + Rank rank = findRank(lotto, winnerNumber, bonusNumber); + int lottoCount = result.get(rank) + 1; + + result.put(rank, lottoCount); + } + } + + private Rank findRank(Lotto lotto, WinnerNumber winnerNumber, BonusNumber bonusNumber) { + long winnerMatch = countMatchingWinnerNumber(lotto, winnerNumber); + boolean bonusMatch = matchingBonusNumber(lotto, bonusNumber); + + return Rank.findRank(winnerMatch, bonusMatch); + } + + private boolean matchingBonusNumber(Lotto lotto, BonusNumber bonusNumber) { + return lotto.getNumbers().stream() + .anyMatch(number -> number.equals(bonusNumber.getBonusNumber())); + } + + private long countMatchingWinnerNumber(Lotto lotto, WinnerNumber winnerNumber) { + return lotto.getNumbers().stream() + .filter(winnerNumber.getWinnerNumbers()::contains) + .count(); + } +} diff --git a/src/main/java/lotto/model/WinnerNumber.java b/src/main/java/lotto/model/WinnerNumber.java new file mode 100644 index 00000000000..5f5d59a7ea9 --- /dev/null +++ b/src/main/java/lotto/model/WinnerNumber.java @@ -0,0 +1,51 @@ +package lotto.model; + +import static lotto.constant.Constant.LOTTO_SIZE; +import static lotto.constant.Constant.MAX_LOTTO_NUMBER; +import static lotto.constant.Constant.MIN_LOTTO_NUMBER; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_CAN_NOT_BE_DUPLICATE; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_SIZE_IS_SIX; + +import java.util.List; +import lotto.exception.ErrorInputException; + +public class WinnerNumber { + private final List winnerNumbers; + + private WinnerNumber(List winnerNumbers) { + this.winnerNumbers = winnerNumbers; + validate(); + isDuplicate(); + isBetweenOneAndFortyFive(); + } + + public static WinnerNumber createWinnerNumber(List winnerNumbers) { + return new WinnerNumber(winnerNumbers); + } + + public List getWinnerNumbers() { + return winnerNumbers; + } + + private void validate() { + if (winnerNumbers.size() != LOTTO_SIZE) { + throw new ErrorInputException(WINNER_NUMBER_SIZE_IS_SIX); + } + } + + private void isDuplicate() { + long countDistinct = winnerNumbers.stream().distinct().count(); + if (countDistinct < winnerNumbers.size()) { + throw new ErrorInputException(WINNER_NUMBER_CAN_NOT_BE_DUPLICATE); + } + } + + private void isBetweenOneAndFortyFive() { + boolean allBetweenOneAndFortyFive = winnerNumbers.stream() + .allMatch(integer -> integer >= MIN_LOTTO_NUMBER && integer <= MAX_LOTTO_NUMBER); + if (!allBetweenOneAndFortyFive) { + throw new ErrorInputException(WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE); + } + } +} From 0e93074227f9803a90b95402e0db874b1cbf8e85 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:17:19 +0900 Subject: [PATCH 04/20] :sparkles: [Feat] Validator --- .../validation/BonusNumberValidator.java | 40 +++++++++++++ .../validation/PurchasePriceValidator.java | 40 +++++++++++++ .../validation/WinnerNumberValidator.java | 59 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 src/main/java/lotto/validation/BonusNumberValidator.java create mode 100644 src/main/java/lotto/validation/PurchasePriceValidator.java create mode 100644 src/main/java/lotto/validation/WinnerNumberValidator.java diff --git a/src/main/java/lotto/validation/BonusNumberValidator.java b/src/main/java/lotto/validation/BonusNumberValidator.java new file mode 100644 index 00000000000..b4cf5e554cc --- /dev/null +++ b/src/main/java/lotto/validation/BonusNumberValidator.java @@ -0,0 +1,40 @@ +package lotto.validation; + +import static lotto.constant.Constant.MIN_LOTTO_NUMBER; +import static lotto.constant.Constant.ZERO; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_MORE_THAN_ONE; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_NOT_INTEGER; + +import lotto.exception.ErrorInputException; + +public class BonusNumberValidator { + public void isNullOrEmpty(String input) { + if (input == null || input.length() == ZERO) { + throw new ErrorInputException(BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY); + } + } + + public int isInteger(String input) { + if (!isNumeric(input)) { + throw new ErrorInputException(BONUS_NUMBER_IS_NOT_INTEGER); + } + return Integer.parseInt(input); + } + + private boolean isNumeric(String input) { + try { + Integer.parseInt(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public int moreThanOne(int input) { + if (input < MIN_LOTTO_NUMBER) { + throw new ErrorInputException(BONUS_NUMBER_IS_MORE_THAN_ONE); + } + return input; + } +} diff --git a/src/main/java/lotto/validation/PurchasePriceValidator.java b/src/main/java/lotto/validation/PurchasePriceValidator.java new file mode 100644 index 00000000000..c135f694247 --- /dev/null +++ b/src/main/java/lotto/validation/PurchasePriceValidator.java @@ -0,0 +1,40 @@ +package lotto.validation; + +import static lotto.constant.Constant.MIN_LOTTO_NUMBER; +import static lotto.constant.Constant.ZERO; +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY; +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_IS_MORE_THAN_ONE; +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_IS_NOT_INTEGER; + +import lotto.exception.ErrorInputException; + +public class PurchasePriceValidator { + public void isNullOrEmpty(String input) { + if (input == null || input.length() == ZERO) { + throw new ErrorInputException(PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY); + } + } + + public int isInteger(String input) { + if (!isNumeric(input)) { + throw new ErrorInputException(PURCHASE_PRICE_IS_NOT_INTEGER); + } + return Integer.parseInt(input); + } + + private boolean isNumeric(String input) { + try { + Integer.parseInt(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public int moreThanOne(int input) { + if (input < MIN_LOTTO_NUMBER) { + throw new ErrorInputException(PURCHASE_PRICE_IS_MORE_THAN_ONE); + } + return input; + } +} diff --git a/src/main/java/lotto/validation/WinnerNumberValidator.java b/src/main/java/lotto/validation/WinnerNumberValidator.java new file mode 100644 index 00000000000..75190f24edd --- /dev/null +++ b/src/main/java/lotto/validation/WinnerNumberValidator.java @@ -0,0 +1,59 @@ +package lotto.validation; + +import static lotto.constant.Constant.MIN_LOTTO_NUMBER; +import static lotto.constant.Constant.ZERO; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_ERROR_INPUT; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_MORE_THAN_ONE; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_NOT_INTEGER; + +import java.util.ArrayList; +import java.util.List; +import lotto.exception.ErrorInputException; + +public class WinnerNumberValidator { + public void isNullOrEmpty(String input) { + if (input == null || input.length() == ZERO) { + throw new ErrorInputException(WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY); + } + } + + public void checkCommaError(String input) { + if (input.contains(",,")) { + throw new ErrorInputException(WINNER_NUMBER_ERROR_INPUT); + } + if (!Character.isDigit(input.charAt(input.length() - 1)) || !Character.isDigit(input.charAt(0))) { + throw new ErrorInputException(WINNER_NUMBER_ERROR_INPUT); + } + } + + public List isInteger(String input) { + String[] split = input.replace(" ", "").split(","); + List numbers = new ArrayList<>(); + for (String s : split) { + if (!isNumeric(s)) { + throw new ErrorInputException(WINNER_NUMBER_IS_NOT_INTEGER); + } + numbers.add(Integer.parseInt(s)); + } + return numbers; + } + + + private boolean isNumeric(String input) { + try { + Integer.parseInt(input); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public List moreThanOne(List input) { + boolean allMoreThanOne = input.stream().allMatch(integer -> integer >= MIN_LOTTO_NUMBER); + if (!allMoreThanOne) { + throw new ErrorInputException(WINNER_NUMBER_IS_MORE_THAN_ONE); + } + return input; + } +} From c606ec06eb2ed75d2a11d195bfb9d06ec8222dc9 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:17:43 +0900 Subject: [PATCH 05/20] :sparkles: [Feat] Constant --- src/main/java/lotto/constant/Constant.java | 10 +++++ .../java/lotto/constant/PrintOutMessage.java | 42 ++++++++++++++++++ src/main/java/lotto/constant/Rank.java | 44 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/main/java/lotto/constant/Constant.java create mode 100644 src/main/java/lotto/constant/PrintOutMessage.java create mode 100644 src/main/java/lotto/constant/Rank.java diff --git a/src/main/java/lotto/constant/Constant.java b/src/main/java/lotto/constant/Constant.java new file mode 100644 index 00000000000..cc7abcfa222 --- /dev/null +++ b/src/main/java/lotto/constant/Constant.java @@ -0,0 +1,10 @@ +package lotto.constant; + +public class Constant { + public static int MIN_LOTTO_NUMBER = 1; + public static int MAX_LOTTO_NUMBER = 45; + public static int LOTTO_SIZE = 6; + public static int THOUSAND = 1000; + public static int ZERO = 0; + +} diff --git a/src/main/java/lotto/constant/PrintOutMessage.java b/src/main/java/lotto/constant/PrintOutMessage.java new file mode 100644 index 00000000000..8b8e75d6e33 --- /dev/null +++ b/src/main/java/lotto/constant/PrintOutMessage.java @@ -0,0 +1,42 @@ +package lotto.constant; + +import java.text.NumberFormat; +import lotto.model.PurchasePrice; + +public enum PrintOutMessage { + PLZ_INPUT_PURCHASE_PRICE("구입금액을 입력해 주세요."), + LOTTO_COUNT("개를 구매했습니다."), + PLZ_INPUT_WINNER_NUMBER("당첨 번호를 입력해 주세요."), + PLZ_INPUT_BONUS_NUMBER("보너스 번호를 입력해 주세요."), + STATISTIC("당첨 통계"), + DIVISION_LINE("---"), + EARNING_POINT("총 수익률은 %s%%입니다."), + EMPTY_LINE("\n"), + COUNT("개"); + + public final String message; + + PrintOutMessage(String message) { + this.message = message; + } + + public String getMessage(String matchingCount) { + return String.format(message, matchingCount); + } + + public static String printLottoCount(int count) { + return count + LOTTO_COUNT.message; + } + + + public static String calculateEarningRate(long totalPrize, PurchasePrice purchasePrice) { + double earningRate = (double) totalPrize / purchasePrice.getPrice() * 100; + + String formattedRate = String.format("%.2f", earningRate); + + NumberFormat nf = NumberFormat.getInstance(); + String formattedNumber = nf.format(Double.parseDouble(formattedRate)); + + return EARNING_POINT.getMessage(formattedNumber); + } +} diff --git a/src/main/java/lotto/constant/Rank.java b/src/main/java/lotto/constant/Rank.java new file mode 100644 index 00000000000..d1d7249eef5 --- /dev/null +++ b/src/main/java/lotto/constant/Rank.java @@ -0,0 +1,44 @@ +package lotto.constant; + +import java.util.Arrays; + +public enum Rank { + NO_MATCH(0, 0, false, "0개 일치 (0원) - "), + THREE_MATCH(3, 5000, false, "3개 일치 (5,000원) - "), + FOUR_MATCH(4, 50000, false, "4개 일치 (50,000원) - "), + FIVE_MATCH_NOT_BONUS(5, 1500000, false, "5개 일치 (1,500,000원) - "), + FIVE_MATCH_PLUS_BONUS(5, 30000000, true, "5개 일치, 보너스 볼 일치 (30,000,000원) - "), + SIX_MATCH(6, 2000000000, false, "6개 일치 (2,000,000,000원) - "); + + public final int match; + public final int prizeMoney; + public final boolean needBonus; + public final String message; + + Rank(int match, int prizeMoney, boolean needBonus, String message) { + this.match = match; + this.prizeMoney = prizeMoney; + this.needBonus = needBonus; + this.message = message; + } + + private static Rank getInstance(long totalCount) { + return Arrays.stream(Rank.values()) + .filter(rank -> rank.match == totalCount) + .findAny() + .orElse(NO_MATCH); + } + + public static Rank findRank(long winnerMatch, boolean bonusMatch) { + Rank rank = getInstance(winnerMatch); + + if (winnerMatch == FIVE_MATCH_NOT_BONUS.match) { + if (bonusMatch) { + return FIVE_MATCH_PLUS_BONUS; + } + return FIVE_MATCH_NOT_BONUS; + } + return rank; + } +} + From 3f6d7d96fc6ae7a8a87fc2b7124b7a457a67c775 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:18:00 +0900 Subject: [PATCH 06/20] :sparkles: [Feat] Service --- .../java/lotto/service/BuyLottoService.java | 27 +++++++++++++++++++ .../service/CalculateStatisticService.java | 23 ++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/main/java/lotto/service/BuyLottoService.java create mode 100644 src/main/java/lotto/service/CalculateStatisticService.java diff --git a/src/main/java/lotto/service/BuyLottoService.java b/src/main/java/lotto/service/BuyLottoService.java new file mode 100644 index 00000000000..354b56c5a94 --- /dev/null +++ b/src/main/java/lotto/service/BuyLottoService.java @@ -0,0 +1,27 @@ +package lotto.service; + +import java.util.ArrayList; +import java.util.List; +import lotto.model.Lotto; +import lotto.model.LottoNumbers; +import lotto.model.PurchasePrice; +import lotto.view.output.OutputView; + +public class BuyLottoService { + private final OutputView outputView; + + public BuyLottoService(OutputView outputView) { + this.outputView = outputView; + } + + public LottoNumbers buyLotto(PurchasePrice purchasePrice) { + int lottoCount = purchasePrice.calculateLottoCount(); + List lotto = new ArrayList<>(); + for (int i = 0; i < lottoCount; i++) { + lotto.add(Lotto.createLotto()); + } + LottoNumbers lottoNumbers = LottoNumbers.createLottoNumbers(lotto); + outputView.printLottoCount(lottoCount, lottoNumbers); + return lottoNumbers; + } +} diff --git a/src/main/java/lotto/service/CalculateStatisticService.java b/src/main/java/lotto/service/CalculateStatisticService.java new file mode 100644 index 00000000000..d4378c10965 --- /dev/null +++ b/src/main/java/lotto/service/CalculateStatisticService.java @@ -0,0 +1,23 @@ +package lotto.service; + +import static lotto.constant.Constant.ZERO; + +import java.util.Map; +import lotto.constant.Rank; +import lotto.view.output.OutputView; + +public class CalculateStatisticService { + private final OutputView outputView; + + public CalculateStatisticService(OutputView outputView) { + this.outputView = outputView; + } + + public long calculatePrizeMoney(Map statistics) { + long totalPrizeMoney = ZERO; + for (Rank rank : statistics.keySet()) { + totalPrizeMoney += rank.prizeMoney * statistics.getOrDefault(rank, ZERO); + } + return totalPrizeMoney; + } +} From 0647a60f65d7350d02bb13ecc550d9d0f4ca9739 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:19:06 +0900 Subject: [PATCH 07/20] :sparkles: [Feat] Controller --- .../lotto/controller/LottoController.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/main/java/lotto/controller/LottoController.java diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java new file mode 100644 index 00000000000..98dd86afb47 --- /dev/null +++ b/src/main/java/lotto/controller/LottoController.java @@ -0,0 +1,83 @@ +package lotto.controller; + +import java.util.List; +import lotto.model.BonusNumber; +import lotto.model.LottoNumbers; +import lotto.model.PurchasePrice; +import lotto.model.Statistics; +import lotto.model.WinnerNumber; +import lotto.service.BuyLottoService; +import lotto.service.CalculateStatisticService; +import lotto.view.input.InputView; +import lotto.view.output.OutputView; + +public class LottoController { + private final InputView inputView; + private final OutputView outputView; + private final BuyLottoService buyLottoService; + private final CalculateStatisticService calculateStatisticService; + + public LottoController(InputView inputView, OutputView outputView, BuyLottoService buyLottoService, + CalculateStatisticService calculateStatisticService) { + this.inputView = inputView; + this.outputView = outputView; + this.buyLottoService = buyLottoService; + this.calculateStatisticService = calculateStatisticService; + } + + public void run() { + PurchasePrice purchasePrice = createPurchasePrice(); // 구입 금액 입력 + + LottoNumbers lottoNumbers = buyLottoService.buyLotto(purchasePrice); // 로또 구입 + + WinnerNumber winnerNumber = createWinnerNumber(); // 당첨 번호 입력 + + BonusNumber bonusNumber = createBonusNumber(winnerNumber); // 보너스 번호 입력 + + // 당첨, 보너스 번호 & 로또 번호 비교 + Statistics statistics = Statistics.createStatistics(); + statistics.calculateMatching(lottoNumbers, winnerNumber, bonusNumber); + + outputView.printStatistic(statistics.getResult()); + + long totalPrize = calculateStatisticService.calculatePrizeMoney(statistics.getResult());// 상금 계산 + outputView.printEarningRate(totalPrize, purchasePrice); + } + + private BonusNumber createBonusNumber(WinnerNumber winnerNumber) { + while (true) { + try { + outputView.printBonusNumberMessage(); + int bonus = inputView.inputBonusNumber(); + return BonusNumber.createBonusNumber(winnerNumber.getWinnerNumbers(), bonus); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } + + private WinnerNumber createWinnerNumber() { + while (true) { + try { + outputView.printWinnerNumberMessage(); + List winners = inputView.inputWinnerNumber(); + return WinnerNumber.createWinnerNumber(winners); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } + + private PurchasePrice createPurchasePrice() { + while (true) { + try { + outputView.printPurchasePriceMessage(); + int price = inputView.readPrice(); + PurchasePrice purchasePrice = PurchasePrice.createPurchasePrice(price); + return purchasePrice; + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } +} From 523b7151b5f57d39e1c2972b6863c88ee56976b6 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:19:33 +0900 Subject: [PATCH 08/20] :sparkles: [Feat] OutputView --- .../java/lotto/view/output/OutputView.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/lotto/view/output/OutputView.java diff --git a/src/main/java/lotto/view/output/OutputView.java b/src/main/java/lotto/view/output/OutputView.java new file mode 100644 index 00000000000..19eb153e113 --- /dev/null +++ b/src/main/java/lotto/view/output/OutputView.java @@ -0,0 +1,62 @@ +package lotto.view.output; + +import static lotto.constant.PrintOutMessage.COUNT; +import static lotto.constant.PrintOutMessage.DIVISION_LINE; +import static lotto.constant.PrintOutMessage.EMPTY_LINE; +import static lotto.constant.PrintOutMessage.PLZ_INPUT_BONUS_NUMBER; +import static lotto.constant.PrintOutMessage.PLZ_INPUT_PURCHASE_PRICE; +import static lotto.constant.PrintOutMessage.PLZ_INPUT_WINNER_NUMBER; +import static lotto.constant.PrintOutMessage.STATISTIC; +import static lotto.constant.PrintOutMessage.calculateEarningRate; + +import java.util.Map; +import lotto.constant.PrintOutMessage; +import lotto.constant.Rank; +import lotto.model.Lotto; +import lotto.model.LottoNumbers; +import lotto.model.PurchasePrice; + +public class OutputView { + public void printPurchasePriceMessage() { + System.out.println(PLZ_INPUT_PURCHASE_PRICE.message); + } + + public void printLottoCount(int lottoCount, LottoNumbers lottoNumbers) { + System.out.println(PrintOutMessage.printLottoCount(lottoCount)); + for (Lotto number : lottoNumbers.getNumbers()) { + System.out.println(number.getNumbers()); + } + System.out.println(EMPTY_LINE.message); + } + + public void printWinnerNumberMessage() { + System.out.println(PLZ_INPUT_WINNER_NUMBER.message); + } + + public void printBonusNumberMessage() { + System.out.println(PLZ_INPUT_BONUS_NUMBER.message); + } + + public void printStatistic(Map matchingResult) { + System.out.println(STATISTIC.message); + System.out.println(DIVISION_LINE.message); + + for (Map.Entry entry : matchingResult.entrySet()) { + Rank rank = entry.getKey(); + int lottoCount = entry.getValue(); + + if (!rank.equals(Rank.NO_MATCH)) { + System.out.println(rank.message + lottoCount + COUNT.message); + } + } + } + + + public void printEarningRate(long totalPrize, PurchasePrice purchasePrice) { + System.out.println(calculateEarningRate(totalPrize, purchasePrice)); + } + + public void printErrorMessage(final String message) { + System.out.print(message + EMPTY_LINE.message); + } +} From a57438c815e8cd5568e19cc93491bb7db83a2766 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:20:50 +0900 Subject: [PATCH 09/20] :sparkles: [Feat] Application --- src/main/java/lotto/Application.java | 52 +++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba44..1c66e3555d1 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,57 @@ package lotto; +import lotto.controller.LottoController; +import lotto.service.BuyLottoService; +import lotto.service.CalculateStatisticService; +import lotto.validation.BonusNumberValidator; +import lotto.validation.PurchasePriceValidator; +import lotto.validation.WinnerNumberValidator; +import lotto.view.input.InputView; +import lotto.view.input.template.InputValidatorTemplate; +import lotto.view.output.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + // 객체 생성 + InputView inputView = createInputView(); + OutputView outputView = new OutputView(); + + BuyLottoService buyLottoService = createBuyLottoService(outputView); + CalculateStatisticService calculateStatisticService = createCalculateStatisticService(outputView); + + LottoController lottoController = createLottoController(inputView, outputView, buyLottoService, + calculateStatisticService); + + // 메서드 호출 + executeControllers(lottoController); + } + + private static InputView createInputView() { + PurchasePriceValidator purchasePriceValidator = new PurchasePriceValidator(); + WinnerNumberValidator winnerNumberValidator = new WinnerNumberValidator(); + BonusNumberValidator bonusNumberValidator = new BonusNumberValidator(); + InputValidatorTemplate template = new InputValidatorTemplate(); + return new InputView(purchasePriceValidator, winnerNumberValidator, bonusNumberValidator, template); + } + + private static BuyLottoService createBuyLottoService(OutputView outputView) { + return new BuyLottoService(outputView); + } + + + private static CalculateStatisticService createCalculateStatisticService(OutputView outputView) { + return new CalculateStatisticService(outputView); + } + + + private static LottoController createLottoController(InputView inputView, + OutputView outputView, + BuyLottoService buyLottoService, + CalculateStatisticService calculateStatisticService) { + return new LottoController(inputView, outputView, buyLottoService, calculateStatisticService); + } + + private static void executeControllers(LottoController lottoController) { + lottoController.run(); } } From 5d49890b78cfe42ec9681b5f422445b8dbbeb70b Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Tue, 28 Nov 2023 14:21:11 +0900 Subject: [PATCH 10/20] =?UTF-8?q?:memo:=20[Docs]=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/README.md b/docs/README.md index 9dc33e57d0a..c53522bb087 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ ___ ## 입력 -- [ ] `로또 구입 금액` 입력 +- [X] `로또 구입 금액` 입력 - 비기능적 요구사항 - null 값이거나 빈 값인지 확인한다. @@ -15,7 +15,7 @@ ___ - 구입 금액은 `1,000원 단위`이다. -- [ ] `당첨 번호` 입력 (번호는 쉼표(,)를 기준으로 구분한다.) +- [X] `당첨 번호` 입력 (번호는 쉼표(,)를 기준으로 구분한다.) - 비기능적 요구사항 ``` @@ -28,10 +28,11 @@ ___ - 가장 앞과 가장 뒤 입력 값은 숫자여야 한다. - 기능적 요구사항 + - 당첨 번호는 6개여야 합니다. - 중복된 숫자는 입력 불가하다. - 1 ~ 45 사이 숫자만 입력 가능하다. -- [ ] `보너스 번호` 입력 +- [X] `보너스 번호` 입력 - 비기능적 요구사항 - null 값이거나 빈 값인지 확인한다. @@ -44,12 +45,13 @@ ___ ## 로또 번호 -- [ ] 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. +- [X] 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. - 로또 번호는 랜덤이다. + - 로또 번호는 중복되면 안된다. ## 당첨 통계 -- [ ] 당첨 통계는 다음과 같다. +- [X] 당첨 통계는 다음과 같다. ``` 3개 일치 (5,000원) - 1개 @@ -61,21 +63,21 @@ ___ ``` -- [ ] 수익률 계산 +- [X] 수익률 계산 - 수익률은 소수점 둘째 자리에서 반올림한다. ## 출력 -- [ ] `구입금액을 입력해 주세요.` 출력 +- [X] `구입금액을 입력해 주세요.` 출력 -- [ ] `?개를 구매했습니다.` 출력 +- [X] `?개를 구매했습니다.` 출력 - 로또 번호 출력 (랜덤) -- [ ] `당첨 번호를 입력해 주세요.` 출력 +- [X] `당첨 번호를 입력해 주세요.` 출력 -- [ ] `보너스 번호를 입력해 주세요.` 출력 +- [X] `보너스 번호를 입력해 주세요.` 출력 -- [ ] `당첨 통계` 출력 +- [X] `당첨 통계` 출력 ``` --- 3개 일치 (5,000원) - 1개 @@ -91,5 +93,5 @@ ___ ## 공통 검증 -- [ ] 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. -- [ ] 1,000단위마다 콤마를 찍기 \ No newline at end of file +- [X] 사용자가 잘못된 값을 입력할 경우 `IllegalArgumentException`를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. +- [X] 1,000단위마다 콤마를 찍기 \ No newline at end of file From 74426bf8a662c66c35ec9da9f7d9f90859070f8d Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:11:42 +0900 Subject: [PATCH 11/20] =?UTF-8?q?:recycle:=20[Refactor]=20=EB=B9=88=20?= =?UTF-8?q?=EA=B0=92=EC=9D=B4=EA=B1=B0=EB=82=98=20null=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EA=B2=80=EC=A6=9D=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/validation/BonusNumberValidator.java | 6 +++--- src/main/java/lotto/validation/PurchasePriceValidator.java | 5 ++--- src/main/java/lotto/validation/WinnerNumberValidator.java | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/lotto/validation/BonusNumberValidator.java b/src/main/java/lotto/validation/BonusNumberValidator.java index b4cf5e554cc..f0aae943e85 100644 --- a/src/main/java/lotto/validation/BonusNumberValidator.java +++ b/src/main/java/lotto/validation/BonusNumberValidator.java @@ -1,7 +1,6 @@ package lotto.validation; import static lotto.constant.Constant.MIN_LOTTO_NUMBER; -import static lotto.constant.Constant.ZERO; import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY; import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_MORE_THAN_ONE; import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_NOT_INTEGER; @@ -9,8 +8,9 @@ import lotto.exception.ErrorInputException; public class BonusNumberValidator { - public void isNullOrEmpty(String input) { - if (input == null || input.length() == ZERO) { + + public static void isBlank(String input) { + if (input == null || input.trim().isEmpty()) { throw new ErrorInputException(BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY); } } diff --git a/src/main/java/lotto/validation/PurchasePriceValidator.java b/src/main/java/lotto/validation/PurchasePriceValidator.java index c135f694247..0accb065b6c 100644 --- a/src/main/java/lotto/validation/PurchasePriceValidator.java +++ b/src/main/java/lotto/validation/PurchasePriceValidator.java @@ -1,7 +1,6 @@ package lotto.validation; import static lotto.constant.Constant.MIN_LOTTO_NUMBER; -import static lotto.constant.Constant.ZERO; import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY; import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_IS_MORE_THAN_ONE; import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_IS_NOT_INTEGER; @@ -9,8 +8,8 @@ import lotto.exception.ErrorInputException; public class PurchasePriceValidator { - public void isNullOrEmpty(String input) { - if (input == null || input.length() == ZERO) { + public static void isBlank(String input) { + if (input == null || input.trim().isEmpty()) { throw new ErrorInputException(PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY); } } diff --git a/src/main/java/lotto/validation/WinnerNumberValidator.java b/src/main/java/lotto/validation/WinnerNumberValidator.java index 75190f24edd..cd8e8672f0f 100644 --- a/src/main/java/lotto/validation/WinnerNumberValidator.java +++ b/src/main/java/lotto/validation/WinnerNumberValidator.java @@ -1,7 +1,6 @@ package lotto.validation; import static lotto.constant.Constant.MIN_LOTTO_NUMBER; -import static lotto.constant.Constant.ZERO; import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY; import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_ERROR_INPUT; import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_MORE_THAN_ONE; @@ -12,8 +11,8 @@ import lotto.exception.ErrorInputException; public class WinnerNumberValidator { - public void isNullOrEmpty(String input) { - if (input == null || input.length() == ZERO) { + public static void isBlank(String input) { + if (input == null || input.trim().isEmpty()) { throw new ErrorInputException(WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY); } } From 9633b70596c51484142f533176f80be1021977dd Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:12:53 +0900 Subject: [PATCH 12/20] =?UTF-8?q?:art:=20[Refactor]=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EB=B6=84=EB=A6=AC=20(=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EC=A4=84=EC=9D=B4=EA=B8=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/Application.java | 23 ++++-- .../lotto/controller/BuyLottoController.java | 72 +++++++++++++++++++ .../lotto/controller/LottoController.java | 69 +++--------------- 3 files changed, 98 insertions(+), 66 deletions(-) create mode 100644 src/main/java/lotto/controller/BuyLottoController.java diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index 1c66e3555d1..0872903c8a1 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,6 +1,8 @@ package lotto; +import lotto.controller.BuyLottoController; import lotto.controller.LottoController; +import lotto.dto.BuyLottoDto; import lotto.service.BuyLottoService; import lotto.service.CalculateStatisticService; import lotto.validation.BonusNumberValidator; @@ -19,11 +21,12 @@ public static void main(String[] args) { BuyLottoService buyLottoService = createBuyLottoService(outputView); CalculateStatisticService calculateStatisticService = createCalculateStatisticService(outputView); - LottoController lottoController = createLottoController(inputView, outputView, buyLottoService, - calculateStatisticService); + BuyLottoController buyLottoController = createBuyLottoController(inputView, outputView, buyLottoService); + + LottoController lottoController = createLottoController(inputView, outputView, calculateStatisticService); // 메서드 호출 - executeControllers(lottoController); + executeControllers(buyLottoController, lottoController); } private static InputView createInputView() { @@ -44,14 +47,20 @@ private static CalculateStatisticService createCalculateStatisticService(OutputV } + private static BuyLottoController createBuyLottoController(InputView inputView, + OutputView outputView, + BuyLottoService buyLottoService) { + return new BuyLottoController(inputView, outputView, buyLottoService); + } + private static LottoController createLottoController(InputView inputView, OutputView outputView, - BuyLottoService buyLottoService, CalculateStatisticService calculateStatisticService) { - return new LottoController(inputView, outputView, buyLottoService, calculateStatisticService); + return new LottoController(inputView, outputView, calculateStatisticService); } - private static void executeControllers(LottoController lottoController) { - lottoController.run(); + private static void executeControllers(BuyLottoController buyLottoController, LottoController lottoController) { + BuyLottoDto buyLottoDto = buyLottoController.buyLotto(); + lottoController.getStatistics(buyLottoDto); } } diff --git a/src/main/java/lotto/controller/BuyLottoController.java b/src/main/java/lotto/controller/BuyLottoController.java new file mode 100644 index 00000000000..953e9f621c1 --- /dev/null +++ b/src/main/java/lotto/controller/BuyLottoController.java @@ -0,0 +1,72 @@ +package lotto.controller; + +import java.util.List; +import lotto.dto.BuyLottoDto; +import lotto.model.BonusNumber; +import lotto.model.LottoNumbers; +import lotto.model.PurchasePrice; +import lotto.model.WinnerNumber; +import lotto.service.BuyLottoService; +import lotto.view.input.InputView; +import lotto.view.output.OutputView; + +public class BuyLottoController { + private final InputView inputView; + private final OutputView outputView; + private final BuyLottoService buyLottoService; + + public BuyLottoController(InputView inputView, OutputView outputView, BuyLottoService buyLottoService) { + this.inputView = inputView; + this.outputView = outputView; + this.buyLottoService = buyLottoService; + } + + public BuyLottoDto buyLotto() { + PurchasePrice purchasePrice = createPurchasePrice(); // 구입 금액 입력 + + LottoNumbers lottoNumbers = buyLottoService.buyLotto(purchasePrice); // 로또 구입 + + WinnerNumber winnerNumber = createWinnerNumber(); // 당첨 번호 입력 + + BonusNumber bonusNumber = createBonusNumber(winnerNumber); // 보너스 번호 입력 + + return BuyLottoDto.createBuyLottoDto(purchasePrice, lottoNumbers, winnerNumber, bonusNumber); + } + + private BonusNumber createBonusNumber(WinnerNumber winnerNumber) { + while (true) { + try { + outputView.printBonusNumberMessage(); + int bonus = inputView.inputBonusNumber(); + return BonusNumber.createBonusNumber(winnerNumber.getWinnerNumbers(), bonus); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } + + private WinnerNumber createWinnerNumber() { + while (true) { + try { + outputView.printWinnerNumberMessage(); + List winners = inputView.inputWinnerNumber(); + return WinnerNumber.createWinnerNumber(winners); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } + + private PurchasePrice createPurchasePrice() { + while (true) { + try { + outputView.printPurchasePriceMessage(); + int price = inputView.readPrice(); + PurchasePrice purchasePrice = PurchasePrice.createPurchasePrice(price); + return purchasePrice; + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } +} diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java index 98dd86afb47..377ec23eaa8 100644 --- a/src/main/java/lotto/controller/LottoController.java +++ b/src/main/java/lotto/controller/LottoController.java @@ -1,12 +1,8 @@ package lotto.controller; -import java.util.List; -import lotto.model.BonusNumber; -import lotto.model.LottoNumbers; -import lotto.model.PurchasePrice; +import lotto.dto.BuyLottoDto; +import lotto.dto.StatisticsDto; import lotto.model.Statistics; -import lotto.model.WinnerNumber; -import lotto.service.BuyLottoService; import lotto.service.CalculateStatisticService; import lotto.view.input.InputView; import lotto.view.output.OutputView; @@ -14,70 +10,25 @@ public class LottoController { private final InputView inputView; private final OutputView outputView; - private final BuyLottoService buyLottoService; private final CalculateStatisticService calculateStatisticService; - public LottoController(InputView inputView, OutputView outputView, BuyLottoService buyLottoService, + public LottoController(InputView inputView, OutputView outputView, CalculateStatisticService calculateStatisticService) { this.inputView = inputView; this.outputView = outputView; - this.buyLottoService = buyLottoService; this.calculateStatisticService = calculateStatisticService; } - public void run() { - PurchasePrice purchasePrice = createPurchasePrice(); // 구입 금액 입력 - - LottoNumbers lottoNumbers = buyLottoService.buyLotto(purchasePrice); // 로또 구입 - - WinnerNumber winnerNumber = createWinnerNumber(); // 당첨 번호 입력 - - BonusNumber bonusNumber = createBonusNumber(winnerNumber); // 보너스 번호 입력 - + public void getStatistics(BuyLottoDto buyLottoDto) { // 당첨, 보너스 번호 & 로또 번호 비교 - Statistics statistics = Statistics.createStatistics(); - statistics.calculateMatching(lottoNumbers, winnerNumber, bonusNumber); + Statistics statistics = buyLottoDto.calculateMatching(); - outputView.printStatistic(statistics.getResult()); + StatisticsDto statisticsDto = StatisticsDto.createStatisticsDto(statistics); + outputView.printStatistic(statisticsDto.getStatistics().getResult()); - long totalPrize = calculateStatisticService.calculatePrizeMoney(statistics.getResult());// 상금 계산 - outputView.printEarningRate(totalPrize, purchasePrice); + long totalPrize = calculateStatisticService.calculatePrizeMoney( + statisticsDto.getStatistics().getResult());// 상금 계산 + outputView.printEarningRate(totalPrize, buyLottoDto.getPurchasePrice()); } - private BonusNumber createBonusNumber(WinnerNumber winnerNumber) { - while (true) { - try { - outputView.printBonusNumberMessage(); - int bonus = inputView.inputBonusNumber(); - return BonusNumber.createBonusNumber(winnerNumber.getWinnerNumbers(), bonus); - } catch (IllegalArgumentException e) { - outputView.printErrorMessage(e.getMessage()); - } - } - } - - private WinnerNumber createWinnerNumber() { - while (true) { - try { - outputView.printWinnerNumberMessage(); - List winners = inputView.inputWinnerNumber(); - return WinnerNumber.createWinnerNumber(winners); - } catch (IllegalArgumentException e) { - outputView.printErrorMessage(e.getMessage()); - } - } - } - - private PurchasePrice createPurchasePrice() { - while (true) { - try { - outputView.printPurchasePriceMessage(); - int price = inputView.readPrice(); - PurchasePrice purchasePrice = PurchasePrice.createPurchasePrice(price); - return purchasePrice; - } catch (IllegalArgumentException e) { - outputView.printErrorMessage(e.getMessage()); - } - } - } } From 7102f24c5de467635b19abf11940c3a644b34e6b Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:13:20 +0900 Subject: [PATCH 13/20] =?UTF-8?q?:recycle:=20[Refactor]=20Dto=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/lotto/dto/BuyLottoDto.java | 40 ++++++++++++++++++++++ src/main/java/lotto/dto/StatisticsDto.java | 19 ++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/main/java/lotto/dto/BuyLottoDto.java create mode 100644 src/main/java/lotto/dto/StatisticsDto.java diff --git a/src/main/java/lotto/dto/BuyLottoDto.java b/src/main/java/lotto/dto/BuyLottoDto.java new file mode 100644 index 00000000000..ebd0552535c --- /dev/null +++ b/src/main/java/lotto/dto/BuyLottoDto.java @@ -0,0 +1,40 @@ +package lotto.dto; + +import lotto.model.BonusNumber; +import lotto.model.LottoNumbers; +import lotto.model.PurchasePrice; +import lotto.model.Statistics; +import lotto.model.WinnerNumber; + +public class BuyLottoDto { + private PurchasePrice purchasePrice; + private LottoNumbers lottoNumbers; + private WinnerNumber winnerNumber; + private BonusNumber bonusNumber; + + private BuyLottoDto(PurchasePrice purchasePrice, LottoNumbers lottoNumbers, WinnerNumber winnerNumber, + BonusNumber bonusNumber) { + this.purchasePrice = purchasePrice; + this.lottoNumbers = lottoNumbers; + this.winnerNumber = winnerNumber; + this.bonusNumber = bonusNumber; + } + + public static BuyLottoDto createBuyLottoDto(PurchasePrice purchasePrice, + LottoNumbers lottoNumbers, + WinnerNumber winnerNumber, + BonusNumber bonusNumber) { + return new BuyLottoDto(purchasePrice, lottoNumbers, winnerNumber, bonusNumber); + } + + public PurchasePrice getPurchasePrice() { + return purchasePrice; + } + + public Statistics calculateMatching() { + Statistics statistics = Statistics.createStatistics(); + statistics.calculateMatching(this.lottoNumbers, this.winnerNumber, this.bonusNumber); + return statistics; + } + +} diff --git a/src/main/java/lotto/dto/StatisticsDto.java b/src/main/java/lotto/dto/StatisticsDto.java new file mode 100644 index 00000000000..75754be1548 --- /dev/null +++ b/src/main/java/lotto/dto/StatisticsDto.java @@ -0,0 +1,19 @@ +package lotto.dto; + +import lotto.model.Statistics; + +public class StatisticsDto { + private Statistics statistics; + + private StatisticsDto(Statistics statistics) { + this.statistics = statistics; + } + + public static StatisticsDto createStatisticsDto(Statistics statistics) { + return new StatisticsDto(statistics); + } + + public Statistics getStatistics() { + return statistics; + } +} From 302b2fa3726f7b64d407d1b6aa603563a83354c3 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:13:45 +0900 Subject: [PATCH 14/20] =?UTF-8?q?:recycle:=20[Refactor]=20getter=20?= =?UTF-8?q?=EC=A7=80=EC=96=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/model/BonusNumber.java | 4 ++-- src/main/java/lotto/model/PurchasePrice.java | 2 +- src/main/java/lotto/model/Statistics.java | 5 +++-- src/main/java/lotto/model/WinnerNumber.java | 3 ++- src/main/java/lotto/view/input/InputView.java | 6 +++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/main/java/lotto/model/BonusNumber.java b/src/main/java/lotto/model/BonusNumber.java index 26a0da8e037..dcef1404fe5 100644 --- a/src/main/java/lotto/model/BonusNumber.java +++ b/src/main/java/lotto/model/BonusNumber.java @@ -18,8 +18,8 @@ private BonusNumber(List winnerNumber, int bonusNumber) { isBetweenOneAndFortyFive(); } - public int getBonusNumber() { - return bonusNumber; + public boolean isSameBonusNumber(BonusNumber other) { + return other.bonusNumber == this.bonusNumber; } public static BonusNumber createBonusNumber(List winnerNumber, int bonusNumber) { diff --git a/src/main/java/lotto/model/PurchasePrice.java b/src/main/java/lotto/model/PurchasePrice.java index e198ce9ee52..e512cae4aff 100644 --- a/src/main/java/lotto/model/PurchasePrice.java +++ b/src/main/java/lotto/model/PurchasePrice.java @@ -10,8 +10,8 @@ public class PurchasePrice { private final int price; private PurchasePrice(int price) { - isDividedByThousand(); this.price = price; + isDividedByThousand(); } public static PurchasePrice createPurchasePrice(int price) { diff --git a/src/main/java/lotto/model/Statistics.java b/src/main/java/lotto/model/Statistics.java index 7010f04e689..ff60ffcbf4e 100644 --- a/src/main/java/lotto/model/Statistics.java +++ b/src/main/java/lotto/model/Statistics.java @@ -2,6 +2,7 @@ import static lotto.constant.Constant.ZERO; +import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -22,7 +23,7 @@ public static Statistics createStatistics() { } public Map getResult() { - return result; + return Collections.unmodifiableMap(result); } public void calculateMatching(LottoNumbers lottoNumbers, WinnerNumber winnerNumber, BonusNumber bonusNumber) { @@ -45,7 +46,7 @@ private Rank findRank(Lotto lotto, WinnerNumber winnerNumber, BonusNumber bonusN private boolean matchingBonusNumber(Lotto lotto, BonusNumber bonusNumber) { return lotto.getNumbers().stream() - .anyMatch(number -> number.equals(bonusNumber.getBonusNumber())); + .anyMatch(number -> number.equals(bonusNumber.isSameBonusNumber(bonusNumber))); } private long countMatchingWinnerNumber(Lotto lotto, WinnerNumber winnerNumber) { diff --git a/src/main/java/lotto/model/WinnerNumber.java b/src/main/java/lotto/model/WinnerNumber.java index 5f5d59a7ea9..2793f43173e 100644 --- a/src/main/java/lotto/model/WinnerNumber.java +++ b/src/main/java/lotto/model/WinnerNumber.java @@ -7,6 +7,7 @@ import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE; import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_SIZE_IS_SIX; +import java.util.Collections; import java.util.List; import lotto.exception.ErrorInputException; @@ -25,7 +26,7 @@ public static WinnerNumber createWinnerNumber(List winnerNumbers) { } public List getWinnerNumbers() { - return winnerNumbers; + return Collections.unmodifiableList(winnerNumbers); } private void validate() { diff --git a/src/main/java/lotto/view/input/InputView.java b/src/main/java/lotto/view/input/InputView.java index 4ccdd970d1b..307c8397296 100644 --- a/src/main/java/lotto/view/input/InputView.java +++ b/src/main/java/lotto/view/input/InputView.java @@ -34,21 +34,21 @@ public int inputBonusNumber() { } private int purchasePriceValidate(String input) { - purchasePriceValidator.isNullOrEmpty(input); + purchasePriceValidator.isBlank(input); int purchasePrice = purchasePriceValidator.isInteger(input); return purchasePriceValidator.moreThanOne(purchasePrice); } private List WinnerNumberValidate(String input) { - winnerNumberValidator.isNullOrEmpty(input); + winnerNumberValidator.isBlank(input); winnerNumberValidator.checkCommaError(input); List winnerNumber = winnerNumberValidator.isInteger(input); return winnerNumberValidator.moreThanOne(winnerNumber); } private int BonusNumberValidate(String input) { - bonusNumberValidator.isNullOrEmpty(input); + bonusNumberValidator.isBlank(input); int bonusNumber = bonusNumberValidator.isInteger(input); return bonusNumberValidator.moreThanOne(bonusNumber); } From 9338062d8edd20b08b4f93619420afe3337fce97 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:15:37 +0900 Subject: [PATCH 15/20] =?UTF-8?q?:recycle:=20[Refactor]=20"[ERROR]=20"=20?= =?UTF-8?q?=EB=AC=B8=EA=B5=AC=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lotto/exception/ErrorInputException.java | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/main/java/lotto/exception/ErrorInputException.java b/src/main/java/lotto/exception/ErrorInputException.java index d9d54343392..c49c6d8efbb 100644 --- a/src/main/java/lotto/exception/ErrorInputException.java +++ b/src/main/java/lotto/exception/ErrorInputException.java @@ -2,37 +2,43 @@ public class ErrorInputException extends IllegalArgumentException { public enum ErrorMessage { - PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY("[ERROR] 구입 금액은 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), - PURCHASE_PRICE_IS_NOT_INTEGER("[ERROR] 구입 금액은 정수만 입력 가능합니다. 다시 입력해 주세요."), - PURCHASE_PRICE_IS_MORE_THAN_ONE("[ERROR] 구입 금액은 1이상의 자연수여야 합니다. 다시 입력해 주세요."), - PURCHASE_PRICE_CAN_DIVIDE_BY_THOUSAND("[ERROR] 구입 금액은 1,000원 단위여야 합니다.다시 입력해 주세요."), + PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY("구입 금액은 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), + PURCHASE_PRICE_IS_NOT_INTEGER("구입 금액은 정수만 입력 가능합니다. 다시 입력해 주세요."), + PURCHASE_PRICE_IS_MORE_THAN_ONE("구입 금액은 1이상의 자연수여야 합니다. 다시 입력해 주세요."), + PURCHASE_PRICE_CAN_DIVIDE_BY_THOUSAND("구입 금액은 1,000원 단위여야 합니다.다시 입력해 주세요."), - LOTTO_NUMBER_CAN_NOT_BE_DUPLICATE("[ERROR] 로또 번호는 중복된 숫자로 구성되면 안됩니다. 다시 입력해 주세요."), + LOTTO_NUMBER_CAN_NOT_BE_DUPLICATE("로또 번호는 중복된 숫자로 구성되면 안됩니다. 다시 입력해 주세요."), - WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY("[ERROR] 당첨 번호는 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), - WINNER_NUMBER_IS_NOT_INTEGER("[ERROR] 당첨 번호는 정수만 입력 가능합니다. 다시 입력해 주세요."), - WINNER_NUMBER_IS_MORE_THAN_ONE("[ERROR] 당첨 번호는 1이상의 자연수여야 합니다. 다시 입력해 주세요."), - WINNER_NUMBER_ERROR_INPUT("[ERROR] 유효하지 않은 입력 입니다. 다시 입력해 주세요."), - WINNER_NUMBER_SIZE_IS_SIX("[ERROR] 당첨 번호는 6개로 구성되어야 합니다. 다시 입력해 주세요."), - WINNER_NUMBER_CAN_NOT_BE_DUPLICATE("[ERROR] 당첨 번호는 중복된 숫자로 구성되면 안됩니다. 다시 입력해 주세요."), - WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE("[ERROR] 당첨 번호는 1 ~ 45 사이 숫자여야 합니다. 다시 입력해 주세요."), + WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY("당첨 번호는 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), + WINNER_NUMBER_IS_NOT_INTEGER("당첨 번호는 정수만 입력 가능합니다. 다시 입력해 주세요."), + WINNER_NUMBER_IS_MORE_THAN_ONE("당첨 번호는 1이상의 자연수여야 합니다. 다시 입력해 주세요."), + WINNER_NUMBER_ERROR_INPUT("유효하지 않은 입력 입니다. 다시 입력해 주세요."), + WINNER_NUMBER_SIZE_IS_SIX("당첨 번호는 6개로 구성되어야 합니다. 다시 입력해 주세요."), + WINNER_NUMBER_CAN_NOT_BE_DUPLICATE("당첨 번호는 중복된 숫자로 구성되면 안됩니다. 다시 입력해 주세요."), + WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE("당첨 번호는 1 ~ 45 사이 숫자여야 합니다. 다시 입력해 주세요."), - BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY("[ERROR] 보너스 번호는 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), - BONUS_NUMBER_IS_NOT_INTEGER("[ERROR] 보너스 번호는 정수만 입력 가능합니다. 다시 입력해 주세요."), - BONUS_NUMBER_IS_MORE_THAN_ONE("[ERROR] 보너스 번호는 1이상의 자연수여야 합니다. 다시 입력해 주세요."), - BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE("[ERROR] 보너스 번호는 1 ~ 45 사이 숫자여야 합니다. 다시 입력해 주세요."), - BONUS_NUMBER_CAN_NOT_BE_DUPLICATE("[ERROR] 보너스 번호는 당첨 번호와 중복된 숫자이면 안됩니다. 다시 입력해 주세요."), + BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY("보너스 번호는 빈 값이거나 null 값 일 수 없습니다. 다시 입력해 주세요."), + BONUS_NUMBER_IS_NOT_INTEGER("보너스 번호는 정수만 입력 가능합니다. 다시 입력해 주세요."), + BONUS_NUMBER_IS_MORE_THAN_ONE("보너스 번호는 1이상의 자연수여야 합니다. 다시 입력해 주세요."), + BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE("보너스 번호는 1 ~ 45 사이 숫자여야 합니다. 다시 입력해 주세요."), + BONUS_NUMBER_CAN_NOT_BE_DUPLICATE("보너스 번호는 당첨 번호와 중복된 숫자이면 안됩니다. 다시 입력해 주세요."), - CAN_NOT_STATISTIC("[ERROR] 당첨 조회가 불가능한 조긴입니다."); + CAN_NOT_STATISTIC("당첨 조회가 불가능한 조건입니다."); ErrorMessage(final String message) { this.message = message; } + private final String errorMessage = "[ERROR] "; + private final String message; + public String getErrorMessage() { + return errorMessage; + } + public String getMessage() { return message; } @@ -40,6 +46,6 @@ public String getMessage() { } public ErrorInputException(ErrorMessage message) { - super(message.getMessage()); + super(message.getErrorMessage() + message.getMessage()); } } From 1424483ca25cc6efcbdd00989d72dac47b24300c Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:16:01 +0900 Subject: [PATCH 16/20] :white_check_mark: [Test] model Test --- .../java/lotto/model/BonusNumberTest.java | 55 +++++++++++++++++++ .../java/lotto/{ => model}/LottoTest.java | 33 +++++++++-- .../java/lotto/model/PurchasePriceTest.java | 33 +++++++++++ .../java/lotto/model/WinnerNumberTest.java | 54 ++++++++++++++++++ 4 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 src/test/java/lotto/model/BonusNumberTest.java rename src/test/java/lotto/{ => model}/LottoTest.java (53%) create mode 100644 src/test/java/lotto/model/PurchasePriceTest.java create mode 100644 src/test/java/lotto/model/WinnerNumberTest.java diff --git a/src/test/java/lotto/model/BonusNumberTest.java b/src/test/java/lotto/model/BonusNumberTest.java new file mode 100644 index 00000000000..1e9a82430f7 --- /dev/null +++ b/src/test/java/lotto/model/BonusNumberTest.java @@ -0,0 +1,55 @@ +package lotto.model; + +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_CAN_NOT_BE_DUPLICATE; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class BonusNumberTest { + + private List lotto; + + @BeforeEach + void setLotto() { + lotto = List.of(1, 2, 3, 4, 5, 6); + } + + @DisplayName("보너스 번호와 로또 번호가 중복되면 예외가 발생한다.") + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4, 5, 6}) + void isDuplicate_Test(int bonusNumber) { + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> BonusNumber.createBonusNumber(lotto, bonusNumber)); + + //then + assertTrue(exception.getMessage().contains(BONUS_NUMBER_CAN_NOT_BE_DUPLICATE.getMessage())); + } + + @DisplayName("보너스 번호가 1 ~ 45 사이가 아니면 예외가 발생한다.") + @ParameterizedTest + @ValueSource(ints = {-1, 0, 46, 100}) + void isBetweenOneAndFortyFive_Test(int bonusNumber) { + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> BonusNumber.createBonusNumber(lotto, bonusNumber)); + + //then + assertTrue(exception.getMessage().contains(BONUS_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE.getMessage())); + } + + @DisplayName("보너스 번호 정상 생성 테스트") + @ParameterizedTest + @ValueSource(ints = {7, 8, 9, 10, 15, 40}) + void general_Create_BonusNumber(int bonusNumber) { + //then + assertDoesNotThrow(() -> BonusNumber.createBonusNumber(lotto, bonusNumber)); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/model/LottoTest.java similarity index 53% rename from src/test/java/lotto/LottoTest.java rename to src/test/java/lotto/model/LottoTest.java index 9f5dfe7eb83..ff214b8f7f5 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/model/LottoTest.java @@ -1,13 +1,22 @@ -package lotto; +package lotto.model; +import static lotto.constant.Constant.LOTTO_SIZE; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.util.List; +class LottoTest { + private Lotto lotto; -import static org.assertj.core.api.Assertions.assertThatThrownBy; + @BeforeEach + void createLotto() { + lotto = Lotto.createLotto(); + } -class LottoTest { @DisplayName("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.") @Test void createLottoByOverSize() { @@ -23,5 +32,19 @@ void createLottoByDuplicatedNumber() { .isInstanceOf(IllegalArgumentException.class); } - // 아래에 추가 테스트 작성 가능 + @DisplayName("로또는 6개이다.") + @Test + void lottoSizeMustBeSix_Test() { + //then + assertEquals(LOTTO_SIZE, lotto.getNumbers().size()); + } + + @DisplayName("로또는 서로 중복된 숫자가 없다.") + @Test + void no_DuplicateNumber_In_Lotto_Test() { + //then + long count = lotto.getNumbers().stream().distinct().count(); + + assertEquals(LOTTO_SIZE, count); + } } \ No newline at end of file diff --git a/src/test/java/lotto/model/PurchasePriceTest.java b/src/test/java/lotto/model/PurchasePriceTest.java new file mode 100644 index 00000000000..3afe740c2e2 --- /dev/null +++ b/src/test/java/lotto/model/PurchasePriceTest.java @@ -0,0 +1,33 @@ +package lotto.model; + +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_CAN_DIVIDE_BY_THOUSAND; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class PurchasePriceTest { + + @DisplayName("구입 금액이 1,000원 단위가 아니면 예외가 발생한다.") + @ParameterizedTest + @ValueSource(ints = {999, 10001, 19999, 20001}) + void not_Divided_By_Thousand(int purchasePrice) { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> PurchasePrice.createPurchasePrice(purchasePrice)); + + //then + assertTrue(exception.getMessage().contains(PURCHASE_PRICE_CAN_DIVIDE_BY_THOUSAND.getMessage())); + } + + @DisplayName("구입 금액이 1,000원 단위라면 정상 작동한다.") + @ParameterizedTest + @ValueSource(ints = {1000, 10000, 20000}) + void Divided_By_Thousand(int purchasePrice) { + //then + assertDoesNotThrow(() -> PurchasePrice.createPurchasePrice(purchasePrice)); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/model/WinnerNumberTest.java b/src/test/java/lotto/model/WinnerNumberTest.java new file mode 100644 index 00000000000..22515c4b498 --- /dev/null +++ b/src/test/java/lotto/model/WinnerNumberTest.java @@ -0,0 +1,54 @@ +package lotto.model; + +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_CAN_NOT_BE_DUPLICATE; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_SIZE_IS_SIX; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class WinnerNumberTest { + @DisplayName("당첨 번호가 중복되면 예외가 발생한다.") + @Test + void isDuplicate_Test() { + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> WinnerNumber.createWinnerNumber(List.of(1, 2, 3, 4, 5, 5))); + + //then + assertTrue(exception.getMessage().contains(WINNER_NUMBER_CAN_NOT_BE_DUPLICATE.getMessage())); + } + + @DisplayName("당첨 번호가 1 ~ 45 사이가 아니면 예외가 발생한다.") + @Test + void isBetweenOneAndFortyFive_Test() { + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> WinnerNumber.createWinnerNumber(List.of(-1, 0, 46, 100, 55, 60))); + + //then + assertTrue(exception.getMessage().contains(WINNER_NUMBER_IS_BETWEEN_ONE_AND_FORTY_FIVE.getMessage())); + } + + @DisplayName("당첨 번호가 6개가 아니면 예외가 발생한다.") + @Test + void winnerNumber_SizeMustBeSix_Test() { + //when + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> WinnerNumber.createWinnerNumber(List.of(1, 2, 3, 4, 5))); + + //then + assertTrue(exception.getMessage().contains(WINNER_NUMBER_SIZE_IS_SIX.getMessage())); + } + + @DisplayName("당첨 번호 정상 생성 테스트") + @Test + void general_Create_WinnerNumber() { + //then + assertDoesNotThrow(() -> WinnerNumber.createWinnerNumber(List.of(1, 2, 3, 4, 5, 6))); + } +} \ No newline at end of file From df51b4d2e4b705e67ec3b78f0991f68c795739d0 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:16:09 +0900 Subject: [PATCH 17/20] :white_check_mark: [Test] validation Test --- .../validation/BonusNumberValidatorTest.java | 54 ++++++++++++++ .../PurchasePriceValidatorTest.java | 55 +++++++++++++++ .../validation/WinnerNumberValidatorTest.java | 70 +++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 src/test/java/lotto/validation/BonusNumberValidatorTest.java create mode 100644 src/test/java/lotto/validation/PurchasePriceValidatorTest.java create mode 100644 src/test/java/lotto/validation/WinnerNumberValidatorTest.java diff --git a/src/test/java/lotto/validation/BonusNumberValidatorTest.java b/src/test/java/lotto/validation/BonusNumberValidatorTest.java new file mode 100644 index 00000000000..cdd102e2b07 --- /dev/null +++ b/src/test/java/lotto/validation/BonusNumberValidatorTest.java @@ -0,0 +1,54 @@ +package lotto.validation; + +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_MORE_THAN_ONE; +import static lotto.exception.ErrorInputException.ErrorMessage.BONUS_NUMBER_IS_NOT_INTEGER; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class BonusNumberValidatorTest { + private final BonusNumberValidator bonusNumberValidator; + + BonusNumberValidatorTest() { + bonusNumberValidator = new BonusNumberValidator(); + } + + @DisplayName("보너스 번호는 빈 값이거나 null이면 예외가 발생한다.") + @Test + void isNullOrEmptyTest() { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> bonusNumberValidator.isBlank(null)); + + //then + assertTrue(exception.getMessage().contains(BONUS_NUMBER_CANNOT_BE_NULL_OR_EMPTY.getMessage())); + } + + @DisplayName("보너스 번호로 정수가 아닌 값을 입력하면 예외가 발생한다.") + @Test + void isIntegerTest() { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> bonusNumberValidator.isInteger("Not Integer")); + + //then + assertTrue(exception.getMessage().contains(BONUS_NUMBER_IS_NOT_INTEGER.getMessage())); + } + + @DisplayName("보너스 번호는 1보다 큰 자연수가 아니면 예외가 발생한다.") + @ParameterizedTest + @ValueSource(ints = {-1, 0, -100}) + void bonusNumber_Greater_Than_One(int input) { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> bonusNumberValidator.moreThanOne(input)); + + //then + assertTrue(exception.getMessage().contains(BONUS_NUMBER_IS_MORE_THAN_ONE.getMessage())); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/validation/PurchasePriceValidatorTest.java b/src/test/java/lotto/validation/PurchasePriceValidatorTest.java new file mode 100644 index 00000000000..e0b16554883 --- /dev/null +++ b/src/test/java/lotto/validation/PurchasePriceValidatorTest.java @@ -0,0 +1,55 @@ +package lotto.validation; + +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY; +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_IS_MORE_THAN_ONE; +import static lotto.exception.ErrorInputException.ErrorMessage.PURCHASE_PRICE_IS_NOT_INTEGER; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class PurchasePriceValidatorTest { + private final PurchasePriceValidator purchasePriceValidator; + + PurchasePriceValidatorTest() { + purchasePriceValidator = new PurchasePriceValidator(); + } + + @DisplayName("구입 금액은 빈 값이거나 null이면 예외가 발생한다.") + @Test + void isNullOrEmptyTest() { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> purchasePriceValidator.isBlank(null)); + + //then + assertTrue(exception.getMessage().contains(PURCHASE_PRICE_CANNOT_BE_NULL_OR_EMPTY.getMessage())); + } + + @DisplayName("구입 금액으로 정수가 아닌 값을 입력하면 예외가 발생한다.") + @Test + void isIntegerTest() { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> purchasePriceValidator.isInteger("Not Integer")); + + //then + assertTrue(exception.getMessage().contains(PURCHASE_PRICE_IS_NOT_INTEGER.getMessage())); + } + + @DisplayName("구입 금액은 1보다 큰 자연수가 아니면 예외가 발생한다.") + @ParameterizedTest + @ValueSource(ints = {-1, 0, -100}) + void purchasePrice_Greater_Than_One(int input) { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> purchasePriceValidator.moreThanOne(input)); + + //then + assertTrue(exception.getMessage().contains(PURCHASE_PRICE_IS_MORE_THAN_ONE.getMessage())); + } + +} \ No newline at end of file diff --git a/src/test/java/lotto/validation/WinnerNumberValidatorTest.java b/src/test/java/lotto/validation/WinnerNumberValidatorTest.java new file mode 100644 index 00000000000..b5e5ac1b606 --- /dev/null +++ b/src/test/java/lotto/validation/WinnerNumberValidatorTest.java @@ -0,0 +1,70 @@ +package lotto.validation; + +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_ERROR_INPUT; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_MORE_THAN_ONE; +import static lotto.exception.ErrorInputException.ErrorMessage.WINNER_NUMBER_IS_NOT_INTEGER; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class WinnerNumberValidatorTest { + private final WinnerNumberValidator winnerNumberValidator; + + WinnerNumberValidatorTest() { + winnerNumberValidator = new WinnerNumberValidator(); + } + + @DisplayName("당첨 번호는 빈 값이거나 null이면 예외가 발생한다.") + @Test + void isNullOrEmptyTest() { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> winnerNumberValidator.isBlank(null)); + + //then + assertTrue(exception.getMessage().contains(WINNER_NUMBER_CANNOT_BE_NULL_OR_EMPTY.getMessage())); + } + + @DisplayName("당첨 번호로 정수가 아닌 값을 입력하면 예외가 발생한다.") + @Test + void isIntegerTest() { + //given + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> winnerNumberValidator.isInteger("Not Integer")); + + //then + assertTrue(exception.getMessage().contains(WINNER_NUMBER_IS_NOT_INTEGER.getMessage())); + } + + @DisplayName("당첨 번호는 1보다 큰 자연수가 아니면 예외가 발생한다.") + @Test + void bonusNumber_Greater_Than_One() { + //given + List input = List.of(-1, 0, 1, 2, 3, 4); + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> winnerNumberValidator.moreThanOne(input)); + + //then + assertTrue(exception.getMessage().contains(WINNER_NUMBER_IS_MORE_THAN_ONE.getMessage())); + } + + @DisplayName("당첨 번호 입력시 ,,를 입력하거나 가장 앞과 뒤는 숫자로 입력하지 않으면 예외가 발생한다.") + @ParameterizedTest + @ValueSource(strings = {"1,2,3,4,,5,6", "1,2,3,4,5,6,", ",1,2,3,4,5,6"}) + void double_Comma_Test(String input) { + //given + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + () -> winnerNumberValidator.checkCommaError(input)); + + //then + assertTrue(exception.getMessage().contains(WINNER_NUMBER_ERROR_INPUT.getMessage())); + } +} \ No newline at end of file From 898c0d040b2f3b69d146d2ddef54ff1c6cd2dbd4 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Wed, 29 Nov 2023 14:16:19 +0900 Subject: [PATCH 18/20] :white_check_mark: [Test] Service Test --- .../lotto/service/BuyLottoServiceTest.java | 31 ++++++++++ .../CalculateStatisticServiceTest.java | 57 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/test/java/lotto/service/BuyLottoServiceTest.java create mode 100644 src/test/java/lotto/service/CalculateStatisticServiceTest.java diff --git a/src/test/java/lotto/service/BuyLottoServiceTest.java b/src/test/java/lotto/service/BuyLottoServiceTest.java new file mode 100644 index 00000000000..44c2d2587d7 --- /dev/null +++ b/src/test/java/lotto/service/BuyLottoServiceTest.java @@ -0,0 +1,31 @@ +package lotto.service; + +import lotto.model.LottoNumbers; +import lotto.model.PurchasePrice; +import lotto.view.output.OutputView; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class BuyLottoServiceTest { + private final BuyLottoService buyLottoService; + + BuyLottoServiceTest() { + buyLottoService = new BuyLottoService(new OutputView()); + } + + @DisplayName("로또 구입 비즈니스 로직 테스트") + @ParameterizedTest + @ValueSource(ints = {7000, 8000, 20000}) + void buyLotto_Test(int input) { + //given + PurchasePrice purchasePrice = PurchasePrice.createPurchasePrice(input); + //when + LottoNumbers lottoNumbers = buyLottoService.buyLotto(purchasePrice); + + //then + Assertions.assertEquals(input / 1000, lottoNumbers.getNumbers().size()); + } + +} \ No newline at end of file diff --git a/src/test/java/lotto/service/CalculateStatisticServiceTest.java b/src/test/java/lotto/service/CalculateStatisticServiceTest.java new file mode 100644 index 00000000000..fa857ea7255 --- /dev/null +++ b/src/test/java/lotto/service/CalculateStatisticServiceTest.java @@ -0,0 +1,57 @@ +package lotto.service; + +import java.util.ArrayList; +import java.util.List; +import lotto.dto.BuyLottoDto; +import lotto.model.BonusNumber; +import lotto.model.Lotto; +import lotto.model.LottoNumbers; +import lotto.model.PurchasePrice; +import lotto.model.Statistics; +import lotto.model.WinnerNumber; +import lotto.view.output.OutputView; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class CalculateStatisticServiceTest { + private final CalculateStatisticService calculateStatisticService; + + CalculateStatisticServiceTest() { + calculateStatisticService = new CalculateStatisticService(new OutputView()); + } + + @DisplayName("총 수익금 계산") + @Test + void calculate_Total_Money_Test() { + //given + PurchasePrice purchasePrice = PurchasePrice.createPurchasePrice(8000); + List lottos = createLotto(); + + LottoNumbers lottoNumbers = LottoNumbers.createLottoNumbers(lottos); + WinnerNumber winnerNumber = WinnerNumber.createWinnerNumber(List.of(1, 2, 3, 4, 5, 6)); + BonusNumber bonusNumber = BonusNumber.createBonusNumber(winnerNumber.getWinnerNumbers(), 7); + BuyLottoDto buyLottoDto = BuyLottoDto.createBuyLottoDto(purchasePrice, lottoNumbers, winnerNumber, bonusNumber); + Statistics statistics = buyLottoDto.calculateMatching(); + + //when + long totalMoney = calculateStatisticService.calculatePrizeMoney(statistics.getResult()); + + //then + Assertions.assertEquals(5000, totalMoney); + } + + private static List createLotto() { + List lottos = new ArrayList<>(); + lottos.add(new Lotto(List.of(1, 2, 3, 7, 8, 9))); + lottos.add(new Lotto(List.of(11, 12, 13, 17, 18, 19))); + lottos.add(new Lotto(List.of(21, 22, 23, 27, 28, 29))); + lottos.add(new Lotto(List.of(31, 32, 33, 37, 38, 39))); + lottos.add(new Lotto(List.of(41, 42, 43, 45, 34, 40))); + lottos.add(new Lotto(List.of(12, 22, 33, 37, 28, 19))); + lottos.add(new Lotto(List.of(13, 22, 39, 17, 28, 12))); + lottos.add(new Lotto(List.of(14, 32, 31, 17, 18, 19))); + return lottos; + } + +} \ No newline at end of file From 355817f8a6b7ab9eff7c6d12933010e623f99a17 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Fri, 1 Dec 2023 20:50:49 +0900 Subject: [PATCH 19/20] =?UTF-8?q?:sparkles:=20[Feat]=20=EB=A1=9C=EB=98=90?= =?UTF-8?q?=20=EB=B2=88=ED=98=B8=20=EC=98=A4=EB=A6=84=EC=B0=A8=EC=88=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 1 + src/main/java/lotto/model/Lotto.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/docs/README.md b/docs/README.md index c53522bb087..5eeb9e76c43 100644 --- a/docs/README.md +++ b/docs/README.md @@ -48,6 +48,7 @@ ___ - [X] 로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다. - 로또 번호는 랜덤이다. - 로또 번호는 중복되면 안된다. + - 로또 번호는 오름차순 정렬되어 출력된다. ## 당첨 통계 diff --git a/src/main/java/lotto/model/Lotto.java b/src/main/java/lotto/model/Lotto.java index 41a7e23ee22..0fac512f3d1 100644 --- a/src/main/java/lotto/model/Lotto.java +++ b/src/main/java/lotto/model/Lotto.java @@ -16,6 +16,7 @@ public class Lotto { public Lotto(List numbers) { validate(numbers); isDuplicate(numbers); + sortLotto(numbers); this.numbers = numbers; } @@ -40,4 +41,8 @@ private void isDuplicate(List numbers) { throw new ErrorInputException(LOTTO_NUMBER_CAN_NOT_BE_DUPLICATE); } } + + private void sortLotto(List numbers) { + Collections.sort(numbers); + } } From ea278eb7b0b56074167223725e8af99ae708a114 Mon Sep 17 00:00:00 2001 From: JHyun0302 Date: Mon, 11 Dec 2023 17:40:41 +0900 Subject: [PATCH 20/20] :sparkles: [Feat] Add Config --- src/main/java/lotto/Application.java | 54 ++----------------- src/main/java/lotto/config/Configuration.java | 37 +++++++++++++ 2 files changed, 41 insertions(+), 50 deletions(-) create mode 100644 src/main/java/lotto/config/Configuration.java diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index 0872903c8a1..33533b1f5c3 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,64 +1,18 @@ package lotto; +import lotto.config.Configuration; import lotto.controller.BuyLottoController; import lotto.controller.LottoController; import lotto.dto.BuyLottoDto; -import lotto.service.BuyLottoService; -import lotto.service.CalculateStatisticService; -import lotto.validation.BonusNumberValidator; -import lotto.validation.PurchasePriceValidator; -import lotto.validation.WinnerNumberValidator; -import lotto.view.input.InputView; -import lotto.view.input.template.InputValidatorTemplate; -import lotto.view.output.OutputView; public class Application { public static void main(String[] args) { - // 객체 생성 - InputView inputView = createInputView(); - OutputView outputView = new OutputView(); - - BuyLottoService buyLottoService = createBuyLottoService(outputView); - CalculateStatisticService calculateStatisticService = createCalculateStatisticService(outputView); - - BuyLottoController buyLottoController = createBuyLottoController(inputView, outputView, buyLottoService); - - LottoController lottoController = createLottoController(inputView, outputView, calculateStatisticService); - - // 메서드 호출 + Configuration configuration = new Configuration(); + LottoController lottoController = configuration.getLottoController(); + BuyLottoController buyLottoController = configuration.getBuyLottoController(); executeControllers(buyLottoController, lottoController); } - private static InputView createInputView() { - PurchasePriceValidator purchasePriceValidator = new PurchasePriceValidator(); - WinnerNumberValidator winnerNumberValidator = new WinnerNumberValidator(); - BonusNumberValidator bonusNumberValidator = new BonusNumberValidator(); - InputValidatorTemplate template = new InputValidatorTemplate(); - return new InputView(purchasePriceValidator, winnerNumberValidator, bonusNumberValidator, template); - } - - private static BuyLottoService createBuyLottoService(OutputView outputView) { - return new BuyLottoService(outputView); - } - - - private static CalculateStatisticService createCalculateStatisticService(OutputView outputView) { - return new CalculateStatisticService(outputView); - } - - - private static BuyLottoController createBuyLottoController(InputView inputView, - OutputView outputView, - BuyLottoService buyLottoService) { - return new BuyLottoController(inputView, outputView, buyLottoService); - } - - private static LottoController createLottoController(InputView inputView, - OutputView outputView, - CalculateStatisticService calculateStatisticService) { - return new LottoController(inputView, outputView, calculateStatisticService); - } - private static void executeControllers(BuyLottoController buyLottoController, LottoController lottoController) { BuyLottoDto buyLottoDto = buyLottoController.buyLotto(); lottoController.getStatistics(buyLottoDto); diff --git a/src/main/java/lotto/config/Configuration.java b/src/main/java/lotto/config/Configuration.java new file mode 100644 index 00000000000..d78f10648f8 --- /dev/null +++ b/src/main/java/lotto/config/Configuration.java @@ -0,0 +1,37 @@ +package lotto.config; + +import lotto.controller.BuyLottoController; +import lotto.controller.LottoController; +import lotto.service.BuyLottoService; +import lotto.service.CalculateStatisticService; +import lotto.validation.BonusNumberValidator; +import lotto.validation.PurchasePriceValidator; +import lotto.validation.WinnerNumberValidator; +import lotto.view.input.InputView; +import lotto.view.input.template.InputValidatorTemplate; +import lotto.view.output.OutputView; + +public class Configuration { + InputView inputView = createInputView(); + OutputView outputView = new OutputView(); + BuyLottoService buyLottoService = new BuyLottoService(outputView); + CalculateStatisticService calculateStatisticService = new CalculateStatisticService(outputView); + BuyLottoController buyLottoController = new BuyLottoController(inputView, outputView, buyLottoService); + LottoController lottoController = new LottoController(inputView, outputView, calculateStatisticService); + + public BuyLottoController getBuyLottoController() { + return buyLottoController; + } + + public LottoController getLottoController() { + return lottoController; + } + + private InputView createInputView() { + PurchasePriceValidator purchasePriceValidator = new PurchasePriceValidator(); + WinnerNumberValidator winnerNumberValidator = new WinnerNumberValidator(); + BonusNumberValidator bonusNumberValidator = new BonusNumberValidator(); + InputValidatorTemplate template = new InputValidatorTemplate(); + return new InputView(purchasePriceValidator, winnerNumberValidator, bonusNumberValidator, template); + } +}