-
Notifications
You must be signed in to change notification settings - Fork 0
[재구현] PR #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mijisu
Are you sure you want to change the base?
[재구현] PR #1
Changes from all commits
488847e
aa1a069
13e8413
118dfb3
ede6fb1
c6e618d
5c8c960
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # 구현할 기능 목록 | ||
| - [X] 구입 금액을 입력받고, 구입 금액에 해당하는 만큼 로또를 발행한다. | ||
| - [X] 로또 1장의 가격은 1,000원이다. 1,000원으로 나누어 떨어지지 않는 경우 예외 처리한다. | ||
|
|
||
|
|
||
| - [X] 발행한 로또 수량 및 번호를 출력한다. | ||
| - [X] 로또 번호는 오름차순으로 정렬하여 보여준다. | ||
| - [X] 로또 번호의 숫자 범위는 1~45까지이다. | ||
| - [X] 1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다. | ||
|
|
||
|
|
||
| - [X] 당첨 번호를 입력받는다. | ||
| - [X] 1 이상 45 이하, 중복되지 않는 6개의 숫자인지 검증 | ||
|
|
||
|
|
||
| - [X] 보너스 번호를 입력받는다. | ||
| - [X] 1 이상 45 이하 이고, 당첨 번호와 중복되지 않는 숫자 여야 함 | ||
|
|
||
|
|
||
| - [X] 사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 게임을 종료한다. | ||
| - [X] 당첨 내역을 출력한다. | ||
| - [X] 당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다. | ||
| - 1등: 6개 번호 일치 / 2,000,000,000원 | ||
| - 2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원 | ||
| - 3등: 5개 번호 일치 / 1,500,000원 | ||
| - 4등: 4개 번호 일치 / 50,000원 | ||
| - 5등: 3개 번호 일치 / 5,000원 | ||
|
|
||
| - [X] 수익률을 출력한다. | ||
| - [X] 수익률은 소수점 둘째 자리에서 반올림한다. | ||
| - (ex. 100.0%, 51.5%, 1,000,000.0%) | ||
|
|
||
|
|
||
| - [X] 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다. | ||
|
|
||
|
|
||
| --- | ||
| ## 전체 흐름 | ||
| ``` | ||
| 구입금액을 입력해 주세요. | ||
| 8000 | ||
|
|
||
| 8개를 구매했습니다. | ||
| [8, 21, 23, 41, 42, 43] | ||
| [3, 5, 11, 16, 32, 38] | ||
| [7, 11, 16, 35, 36, 44] | ||
| [1, 8, 11, 31, 41, 42] | ||
| [13, 14, 16, 38, 42, 45] | ||
| [7, 11, 30, 40, 42, 43] | ||
| [2, 13, 22, 32, 38, 45] | ||
| [1, 3, 5, 14, 22, 45] | ||
|
|
||
| 당첨 번호를 입력해 주세요. | ||
| 1,2,3,4,5,6 | ||
|
|
||
| 보너스 번호를 입력해 주세요. | ||
| 7 | ||
|
|
||
| 당첨 통계 | ||
| --- | ||
| 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%입니다. | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,12 @@ | ||
| package lotto; | ||
|
|
||
| import camp.nextstep.edu.missionutils.Console; | ||
| import lotto.controller.MainController; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| // TODO: 프로그램 구현 | ||
| MainController mainController = MainController.create(); | ||
| mainController.run(); | ||
| Console.close(); | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package lotto.constants; | ||
|
|
||
| public class Constants { | ||
| public static final int LOTTO_PRICE = 1000; | ||
| public static final int MIN_LOTTO_NUMBER = 1; | ||
| public static final int MAX_LOTTO_NUMBER = 45; | ||
| public static final int LOTTO_NUMBERS_SIZE = 6; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| package lotto.controller; | ||
|
|
||
| import lotto.domain.*; | ||
| import lotto.dto.LottosDto; | ||
| import lotto.dto.ResultDto; | ||
| import lotto.service.LottoMaker; | ||
| import lotto.utils.Mapper; | ||
| import lotto.view.InputView; | ||
| import lotto.view.OutputView; | ||
|
|
||
| import java.util.List; | ||
| import java.util.function.Supplier; | ||
|
|
||
| public class MainController { | ||
| private final InputView inputView; | ||
| private final OutputView outputView; | ||
|
|
||
| private MainController(InputView inputView, OutputView outputView) { | ||
| this.inputView = inputView; | ||
| this.outputView = outputView; | ||
| } | ||
|
|
||
| public static MainController create() { | ||
| return new MainController(InputView.getInstance(), OutputView.getInstance()); | ||
| } | ||
|
|
||
| public void run() { | ||
| PurchaseAmount purchaseAmount = createPurchaseAmount(); | ||
| Lottos lottos = createLottos(purchaseAmount); | ||
| printLottos(lottos); | ||
| WinningLotto winningLotto = createWinningLotto(); | ||
| printResult(lottos, winningLotto, purchaseAmount); | ||
| } | ||
|
|
||
| private PurchaseAmount createPurchaseAmount() { | ||
| return readUserInput(() -> { | ||
| long input = inputView.readPurchaseAmount(); | ||
| return PurchaseAmount.from(input); | ||
| }); | ||
| } | ||
|
|
||
| private Lottos createLottos(PurchaseAmount purchaseAmount) { | ||
| long quantityOfLotto = purchaseAmount.getQuantityOfLotto(); | ||
| List<Lotto> lottosMade = LottoMaker.makeLottos(quantityOfLotto); | ||
| return Lottos.from(lottosMade); | ||
| } | ||
|
|
||
| private void printLottos(Lottos lottos) { | ||
| LottosDto lottosDto = Mapper.toLottosDto(lottos); | ||
| outputView.printLottos(lottosDto); | ||
| } | ||
|
|
||
| private WinningLotto createWinningLotto() { | ||
| Lotto winningNumbers = createWinningNumbers(); | ||
| return readUserInput(() -> { | ||
| int bonusNumber = inputView.readBonusNumber(); | ||
| return WinningLotto.of(winningNumbers, bonusNumber); | ||
| }); | ||
| } | ||
|
|
||
| private Lotto createWinningNumbers() { | ||
| return readUserInput(() -> { | ||
| List<Integer> numbers = inputView.readWinningNumbers(); | ||
| return new Lotto(numbers); | ||
| }); | ||
| } | ||
|
|
||
| private void printResult(Lottos lottos, WinningLotto winningLotto, PurchaseAmount purchaseAmount) { | ||
| RankResult rankResult = lottos.findRanks(winningLotto); | ||
| ResultDto resultDto = Mapper.toTotalRankDto(purchaseAmount, rankResult); | ||
| outputView.printResult(resultDto); | ||
| } | ||
|
|
||
| private <T> T readUserInput(Supplier<T> supplier) { | ||
| while (true) { | ||
| try { | ||
| return supplier.get(); | ||
| } catch (IllegalArgumentException e) { | ||
| outputView.printError(e.getMessage()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package lotto.domain; | ||
|
|
||
| import java.util.HashSet; | ||
| import java.util.List; | ||
|
|
||
| import static lotto.constants.Constants.*; | ||
| import static lotto.exception.ErrorMessage.*; | ||
|
|
||
| public class Lotto { | ||
| private final List<Integer> numbers; | ||
|
|
||
| public Lotto(List<Integer> numbers) { | ||
| validate(numbers); | ||
| this.numbers = numbers; | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. number를 별도의 클래스로 묶어주고 이를 vo로 설정해주면 비교가 편해질것 같아요 |
||
| private void validate(List<Integer> numbers) { | ||
| validateSize(numbers); | ||
| validateDuplicates(numbers); | ||
| validateRange(numbers); | ||
| } | ||
|
|
||
| private static void validateSize(List<Integer> numbers) { | ||
| if (numbers.size() != LOTTO_NUMBERS_SIZE) { | ||
| throw new IllegalArgumentException(INVALID_LOTTO_NUMBERS_SIZE.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private void validateDuplicates(List<Integer> numbers) { | ||
| HashSet<Integer> uniqueNumbers = new HashSet<>(numbers); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 구성할 수 있을 거 같아요 |
||
| if (numbers.size() != uniqueNumbers.size()) { | ||
| throw new IllegalArgumentException(DUPLICATED_LOTTO_NUMBERS.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| private void validateRange(List<Integer> numbers) { | ||
| boolean isOutOfRange = numbers.stream() | ||
| .anyMatch(number -> number < MIN_LOTTO_NUMBER || number > MAX_LOTTO_NUMBER); | ||
| if (isOutOfRange) { | ||
| throw new IllegalArgumentException(INVALID_LOTTO_NUMBER_RANGE.getMessage()); | ||
| } | ||
| } | ||
|
|
||
| public int getMatchCount(Lotto lotto) { | ||
| return (int) numbers.stream() | ||
| .filter(lotto.numbers::contains) | ||
| .count(); | ||
| } | ||
|
|
||
| public boolean contains(int number) { | ||
| return numbers.contains(number); | ||
| } | ||
|
|
||
| public List<Integer> getNumbers() { | ||
| return List.copyOf(numbers); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package lotto.domain; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Optional; | ||
|
|
||
| public enum LottoRank { | ||
| FIFTH(3, false,5_000), | ||
| FOURTH(4, false,50_000), | ||
| THIRD(5, false,1_500_000), | ||
| SECOND(5, true, 30_000_000), | ||
| FIRST(6, false, 2_000_000_000); | ||
|
|
||
| private final int matchCount; | ||
| private final boolean requiresBonusMatch; | ||
| private final int prize; | ||
|
|
||
| LottoRank(int matchCount, boolean requiresBonusMatch, int prize) { | ||
| this.matchCount = matchCount; | ||
| this.requiresBonusMatch = requiresBonusMatch; | ||
| this.prize = prize; | ||
| } | ||
|
|
||
| public static Optional<LottoRank> findByMatchResult(int matchCount, boolean bonusMatch) { | ||
| return Arrays.stream(LottoRank.values()) | ||
| .filter(rank -> rank.matchCount == matchCount && (matchCount != 5 || rank.requiresBonusMatch == bonusMatch)) | ||
| .findAny(); | ||
| } | ||
|
|
||
| public int getMatchCount() { | ||
| return matchCount; | ||
| } | ||
|
|
||
| public boolean isRequiresBonusMatch() { | ||
| return requiresBonusMatch; | ||
| } | ||
|
|
||
| public int getPrize() { | ||
| return prize; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package lotto.domain; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| public class Lottos { | ||
| private final List<Lotto> lottos; | ||
|
|
||
| private Lottos(List<Lotto> lottos) { | ||
| this.lottos = lottos; | ||
| } | ||
|
|
||
| public static Lottos from(List<Lotto> lottos) { | ||
| return new Lottos(lottos); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 생성자와 똑같은 역할을 하는 정적 팩토리 메서드가 필요하진 않을 것 같습니다! |
||
| } | ||
|
|
||
| public List<Lotto> getLottos() { | ||
| return List.copyOf(lottos); | ||
| } | ||
|
|
||
| public RankResult findRanks(WinningLotto winningLotto) { | ||
| List<LottoRank> lottoRanks = lottos.stream() | ||
| .map(lotto -> LottoRank.findByMatchResult( | ||
| lotto.getMatchCount(winningLotto.getWinningNumbers()), | ||
| lotto.contains(winningLotto.getBonusNumber()))) | ||
| .flatMap(Optional::stream) | ||
| .toList(); | ||
| return RankResult.from(lottoRanks); | ||
| } | ||
|
|
||
| public long getQuantity() { | ||
| return lottos.size(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package lotto.domain; | ||
|
|
||
| import lotto.utils.PurchaseAmountValidator; | ||
|
|
||
| import static lotto.constants.Constants.LOTTO_PRICE; | ||
|
|
||
| public class PurchaseAmount { | ||
| private final long amount; | ||
|
|
||
| private PurchaseAmount(long amount) { | ||
| this.amount = amount; | ||
| } | ||
|
|
||
| public static PurchaseAmount from(long amount) { | ||
| PurchaseAmountValidator.validatePositive(amount); | ||
| PurchaseAmountValidator.validateDividedByUnit(amount); | ||
| return new PurchaseAmount(amount); | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. validate로직을 lotto 도메인과 다르게 개별 클래스로 분리를 한 이유가 있을까요? |
||
| public long getQuantityOfLotto() { | ||
| return amount / LOTTO_PRICE; | ||
| } | ||
|
|
||
| public long getAmount() { | ||
| return amount; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package lotto.domain; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class RankResult { | ||
| private final List<LottoRank> lottoRanks; | ||
|
|
||
| private RankResult(List<LottoRank> lottoRanks) { | ||
| this.lottoRanks = lottoRanks; | ||
| } | ||
|
|
||
| public static RankResult from(List<LottoRank> lottoRanks) { | ||
| return new RankResult(lottoRanks); | ||
| } | ||
|
|
||
| public long getRankCount(LottoRank rank) { | ||
| return lottoRanks.stream() | ||
| .filter(lottoRank -> lottoRank == rank) | ||
| .count(); | ||
| } | ||
|
|
||
| public long getTotalPrize() { | ||
| return lottoRanks.stream() | ||
| .mapToLong(LottoRank::getPrize) | ||
| .sum(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 인라인으로 처리해도 될 것 같아요!!