-
Notifications
You must be signed in to change notification settings - Fork 5
[로또] 킬리안(박명규) 미션 제출합니다. #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: kilian
Are you sure you want to change the base?
Changes from all commits
af9f84e
7cba8fc
e87a84d
1ce5340
a8479d1
22da92c
7f2caba
8fdc6c4
5b6eb35
d1e16c6
491070d
15168f2
f95bdd6
7e4cefa
15ef237
681ac2a
0a5e3ed
572309c
781d2f1
ea9d762
22a5d55
3f6be4f
d3ecc04
3c51b34
9bc70a9
7b5b04a
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 |
|---|---|---|
| @@ -1,5 +1,24 @@ | ||
| # java-lotto | ||
|
|
||
| 로또 미션 저장소 | ||
| ## 비즈니스 요구 사항 | ||
|
|
||
| # [미션 리드미](https://github.com/talmood/private-mission-README/tree/main/%EB%AF%B8%EC%85%98%203%20-%20%EB%A1%9C%EB%98%90) | ||
| - 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다. | ||
| - 로또 1장의 가격은 1000원이다. | ||
| - 로또 구입 금액은 0보다 커야 한다. | ||
| - 로또는 1개이상 구매해야한다. | ||
| - 지난 주 당첨 번호는 "1, 2, 3, 4, 5, 6" 같은 형식이어야 한다. | ||
| - 보너스 번호는 당첨 번호에 포함되면 안된다. | ||
|
|
||
| ## 구현 기능 목록 | ||
|
|
||
| - [x] 구입 금액 사용자 입력을 받는다. | ||
| - [x] 수동으로 구매할 로또 수를 입력한다. | ||
| - [x] 수동으로 구매할 번호를 입력한다. | ||
| - [x] 구입 금액에 맞춰 로또를 구입 갯수를 계산한다. | ||
| - [x] 구입한 로또의 갯수를 출력한다. | ||
| - [x] 구입한 로또의 목록을 생성한다. | ||
| - [x] 구입한 로또의 목록을 출력한다. | ||
| - [x] 지난 주 당첨 번호를 입력받는다. | ||
| - [x] 보너스 볼을 입력 받는다. | ||
| - [x] 당첨 결과를 판단한다. | ||
| - [x] 당첨 결과를 출력한다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import controller.LottoSimulator; | ||
|
|
||
| public class LottoApplication { | ||
| public static void main(String[] args) { | ||
| new LottoSimulator().run(); | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package constant; | ||
|
|
||
| public abstract class LottoConstants { | ||
|
|
||
| public static final int MIN_LOTTO_NUMBER_INCLUSIVE = 1; | ||
| public static final int MAX_LOTTO_NUMBER_INCLUSIVE = 45; | ||
| public static final int LOTTO_PRICE = 1000; | ||
| public static final int LOTTO_NUMBERS_SIZE = 6; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package controller; | ||
|
|
||
| import domain.*; | ||
| import view.input.ConsoleInputView; | ||
| import view.input.InputView; | ||
| import view.input.dto.*; | ||
| import view.output.ConsoleOutputView; | ||
| import view.output.OutputView; | ||
| import view.output.dto.LottoWinningStatisticsOutput; | ||
| import view.output.dto.LottosOutput; | ||
| import view.output.dto.PurchaseCountOutput; | ||
|
|
||
| public class LottoSimulator { | ||
|
|
||
| public void run() { | ||
| InputView inputView = new ConsoleInputView(); | ||
| OutputView outputView = new ConsoleOutputView(); | ||
|
|
||
| PurchaseInput purchaseInput = inputView.inputPurchaseAmount(); | ||
| PurchaseAmount purchaseAmount = purchaseInput.toPurchaseAmount(); | ||
|
|
||
| ManualPurchaseCountInput manualPurchaseCountInput = inputView.inputManualPurchaseCount(); | ||
| PurchaseCount manualPurchaseCount = manualPurchaseCountInput.toManualPurchaseCount(); | ||
| ManualLottoNumbersInput manualLottoNumbersInput = inputView.inputManualLottoNumbers(manualPurchaseCount.fetchPurchaseCount()); | ||
| PurchaseCountCalculator purchaseCountCalculator = new PurchaseCountCalculator(purchaseAmount); | ||
| PurchaseCount autoPurchaseCount = purchaseCountCalculator.calculateAutoPurchaseCount(manualPurchaseCount); | ||
| outputView.viewPurchaseCount(PurchaseCountOutput.of(manualPurchaseCount, autoPurchaseCount)); | ||
|
Comment on lines
+15
to
+27
Member
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. 이 메서드는 너무 길고 가독성이 낮아 무엇을 하는지 파악하기가 매우 어렵습니다.🤯 (버그를 유발하기 쉬운구조입니다.) 적절한 단위로 묶어서 분리하고, 이름을 붙여서 의도를 드러내보면 좋을 것 같아요!
Author
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. 이 부분 분리해봐야겠어요 |
||
|
|
||
| LottosGenerator lottosGenerator = new LottosGenerator(autoPurchaseCount); | ||
| Lottos manualLottos = manualLottoNumbersInput.toLottos(); | ||
| Lottos autoLottos = lottosGenerator.generate(); | ||
| Lottos lottos = manualLottos.addLottos(autoLottos); | ||
| outputView.viewLottos(LottosOutput.from(lottos)); | ||
|
|
||
| WinningNumbersInput winningNumbersInput = inputView.inputWinningNumbers(); | ||
| WinningNumbers winningNumbers = winningNumbersInput.toWinningNumbers(); | ||
|
|
||
| BonusNumberInput bonusNumberInput = inputView.inputBonusNumber(); | ||
| BonusNumber bonusNumber = bonusNumberInput.toBonusNumber(); | ||
|
|
||
| LottosWinningCalculator lottosWinningCalculator = new LottosWinningCalculator(lottos, winningNumbers, bonusNumber); | ||
| LottoWinnings lottoWinnings = lottosWinningCalculator.calculate(); | ||
| LottoWinningStatisticsOutput lottoWinningStatisticsOutput = LottoWinningStatisticsOutput.from(lottoWinnings, purchaseAmount); | ||
| outputView.viewWinningStatistics(lottoWinningStatisticsOutput); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package domain; | ||
|
|
||
| public class AutoPurchaseCount extends PurchaseCount { | ||
| private AutoPurchaseCount(int purchaseCount) { | ||
| super(purchaseCount); | ||
| } | ||
|
|
||
| public static AutoPurchaseCount create(int purchaseCount) { | ||
| return new AutoPurchaseCount(purchaseCount); | ||
| } | ||
|
Comment on lines
+8
to
+10
Member
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. 이 팩토리 메서드는 생성자를 대신 호출하는 것 외에 별다른 로직이 없네요.
Author
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. 도메인객체를 단순히 생성(create)한다는 가독성을 위해 그렇게 했는데 단순 생성자를 사용하는 것으로도 고려해보겠습니다. |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package domain; | ||
|
|
||
| public class BonusNumber { | ||
|
|
||
| private final int bonusNumber; | ||
|
|
||
| private BonusNumber(int bonusNumber) { | ||
| this.bonusNumber = bonusNumber; | ||
| } | ||
|
|
||
| public static BonusNumber create(int bonusNumber) { | ||
| return new BonusNumber(bonusNumber); | ||
| } | ||
|
Comment on lines
+7
to
+13
Member
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. 어떤 정수도 범위에 상관없이 보너스 번호가 될 수 있는 구조입니다!
Author
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. validation하는 걸 놓친 부분이네요! |
||
|
|
||
| public int fetchBonusNumber() { | ||
| return this.bonusNumber; | ||
| } | ||
|
|
||
| public boolean isSameNumber(int number) { | ||
| return this.bonusNumber == number; | ||
| } | ||
|
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| package domain; | ||
|
|
||
| import exception.DomainValidationException; | ||
|
|
||
| import java.util.HashSet; | ||
| import java.util.List; | ||
|
|
||
| import static constant.LottoConstants.*; | ||
| import static exception.code.ErrorCode.*; | ||
|
|
||
| public class Lotto { | ||
|
|
||
| private final List<Integer> lottoNumbers; | ||
|
|
||
| private Lotto(List<Integer> lottoNumbers) { | ||
| this.validateLottoNumbersSize(lottoNumbers); | ||
| this.validateLottoNumbersUnique(lottoNumbers); | ||
| this.validateLottoNumbersInRange(lottoNumbers); | ||
| this.lottoNumbers = lottoNumbers; | ||
| } | ||
|
|
||
| public static Lotto create(List<Integer> lottoNumbers) { | ||
| return new Lotto(lottoNumbers); | ||
| } | ||
|
|
||
| private void validateLottoNumbersInRange(List<Integer> lottoNumbers) { | ||
| if (!isAllLottoNumberRange(lottoNumbers)) { | ||
| throw new DomainValidationException( | ||
| INVALID_LOTTO_NUMBER_RANGE, | ||
| String.format("로또 번호의 범위는 %d부터 %d까지 입니다.", | ||
| MIN_LOTTO_NUMBER_INCLUSIVE, | ||
| MAX_LOTTO_NUMBER_INCLUSIVE | ||
| ) | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| private void validateLottoNumbersSize(List<Integer> lottoNumbers) { | ||
| if (lottoNumbers.size() != LOTTO_NUMBERS_SIZE) { | ||
| throw new DomainValidationException( | ||
| INVALID_LOTTO_NUMBERS_SIZE, | ||
| String.format("로또 번호의 갯수는 %d개여야 합니다.", LOTTO_NUMBERS_SIZE) | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| private void validateLottoNumbersUnique(List<Integer> lottoNumbers) { | ||
| if (!isUniqueLottoNumbers(lottoNumbers)) { | ||
| throw new DomainValidationException(NOT_UNIQUE_LOTTO_NUMBERS, "로또 번호는 모두 달라야 합니다."); | ||
| } | ||
| } | ||
|
|
||
| private boolean isAllLottoNumberRange(List<Integer> lottoNumbers) { | ||
| return lottoNumbers.stream() | ||
| .allMatch(this::isLottoNumberRange); | ||
| } | ||
|
|
||
| private boolean isLottoNumberRange(Integer number) { | ||
| return number >= MIN_LOTTO_NUMBER_INCLUSIVE && number <= MAX_LOTTO_NUMBER_INCLUSIVE; | ||
| } | ||
|
|
||
| private boolean isUniqueLottoNumbers(List<Integer> lottoNumbers) { | ||
| return new HashSet<>(lottoNumbers).size() == lottoNumbers.size(); | ||
| } | ||
|
|
||
| public List<Integer> fetchLottoNumberList() { | ||
| return List.copyOf(this.lottoNumbers); | ||
| } | ||
|
|
||
| private long countMatchWinningNumbers(WinningNumbers winningNumbers) { | ||
| return winningNumbers.countMatchNumbers(this.lottoNumbers); | ||
| } | ||
|
|
||
| private long countMatchBonusNumber(BonusNumber bonusNumber) { | ||
| return this.lottoNumbers.stream() | ||
| .filter(bonusNumber::isSameNumber) | ||
| .count(); | ||
| } | ||
|
|
||
| public LottoWinning findLottoWinning(WinningNumbers winningNumbers, BonusNumber bonusNumber) { | ||
| long matchedWinningNumberCount = this.countMatchWinningNumbers(winningNumbers); | ||
| long matchedBonusNumberCount = this.countMatchBonusNumber(bonusNumber); | ||
|
|
||
| return LottoWinning.findLottoWinning(matchedWinningNumberCount, matchedBonusNumberCount); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| package domain; | ||
|
|
||
| import java.util.Arrays; | ||
|
|
||
| public enum LottoWinning { | ||
| FIRST_PLACE(2000000000, 6, 0), | ||
| SECOND_PLACE(30000000, 5, 1), | ||
| THIRD_PLACE(1500000, 5, 0), | ||
| FOURTH_PLACE(50000, 4, 0), | ||
| FIFTH_PLACE(5000, 3, 0), | ||
| ELSE_PLACE(0, 0, 0); | ||
|
|
||
| private final long winningAmount; | ||
|
|
||
| private final long matchWinningNumberCount; | ||
|
|
||
| private final long matchBonusNumberCount; | ||
|
|
||
| LottoWinning(long winningAmount, long matchWinningNumberCount, long matchBonusNumberCount) { | ||
| this.winningAmount = winningAmount; | ||
| this.matchWinningNumberCount = matchWinningNumberCount; | ||
| this.matchBonusNumberCount = matchBonusNumberCount; | ||
| } | ||
|
|
||
| public static LottoWinning findLottoWinning(long matchWinningNumberCount, long matchBonusNumberCount) { | ||
| return Arrays.stream(LottoWinning.values()) | ||
| .filter(lottoWinning -> lottoWinning.isMatchLottoWinning(matchWinningNumberCount, matchBonusNumberCount)) | ||
|
Comment on lines
+25
to
+27
Member
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. findLottoWinning을 호출할 때, 실수로 순서를 바꿔서 넣을 가능성이 있는 구조입니다!
Author
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. 저 두 값을 위해 class를 생성해야 되나 생각이 들기도 하네요
Member
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. 실수하기 쉬운 구조에서, 실수하기 매우 어려운 구조로 변경해나가는 것의 비용이라고 치면 가성비 괜찮다고 생각해요~! 🌝 객체지향 생활체조 원칙에도 나와있는 내용인데, 같이 공부해보시죠~! |
||
| .findAny() | ||
| .orElseGet(() -> ELSE_PLACE); | ||
| } | ||
|
Comment on lines
+25
to
+30
Member
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. Optional의 |
||
|
|
||
| private boolean isMatchLottoWinning(long matchWinningNumberCount, long matchBonusNumberCount) { | ||
| return this.isSameMatchWinningCount(matchWinningNumberCount) && this.isSameMatchBonusNumberCount(matchBonusNumberCount); | ||
| } | ||
|
|
||
| private boolean isSameMatchWinningCount(long matchWinningNumberCount) { | ||
| return this.matchWinningNumberCount == matchWinningNumberCount; | ||
| } | ||
|
|
||
| private boolean isSameMatchBonusNumberCount(long matchBonusNumberCount) { | ||
| return this.matchBonusNumberCount == matchBonusNumberCount; | ||
| } | ||
|
|
||
| public long fetchWinningAmount() { | ||
| return this.winningAmount; | ||
| } | ||
|
|
||
| public long fetchMatchWinningNumberCount() { | ||
| return this.matchWinningNumberCount; | ||
| } | ||
|
Comment on lines
+44
to
+50
Member
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. 단순 getter 인데 fetch라는 prefix를 쓰신 이유가 궁금합니다 :)
Author
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. 음.. 이 부분은 어떤 로직이 있다면 fetch를 쓰고 단순 데이터 조회면 getter를 쓰는 방향을 수정해봐야겠네요 |
||
|
|
||
| public boolean isMatchBonusNumber() { | ||
| return this.matchBonusNumberCount > 0; | ||
| } | ||
|
|
||
| public boolean isEqual(LottoWinning lottoWinning) { | ||
| return this == lottoWinning; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package domain; | ||
|
|
||
| import exception.DomainValidationException; | ||
| import exception.code.ErrorCode; | ||
| import util.CollectionUtils; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class LottoWinnings { | ||
|
|
||
| private final List<LottoWinning> lottoWinnings; | ||
|
|
||
| private LottoWinnings(List<LottoWinning> lottoWinnings) { | ||
| this.validateNotEmpty(lottoWinnings); | ||
| this.lottoWinnings = lottoWinnings; | ||
| } | ||
|
|
||
| public static LottoWinnings create(List<LottoWinning> lottoWinnings) { | ||
| return new LottoWinnings(lottoWinnings); | ||
| } | ||
|
|
||
| private void validateNotEmpty(List<LottoWinning> lottoWinnings) { | ||
| if (CollectionUtils.isEmpty(lottoWinnings)) { | ||
| throw new DomainValidationException(ErrorCode.COLLECTION_MUST_NOT_BE_EMPTY, "당첨 정보는 null이거나 empty이면 안됩니다."); | ||
| } | ||
| } | ||
|
|
||
| public long sumAllWinningAmount() { | ||
| return this.lottoWinnings.stream() | ||
| .mapToLong(LottoWinning::fetchWinningAmount) | ||
| .sum(); | ||
| } | ||
|
|
||
| public long countMatchLottoWinning(LottoWinning lottoWinning) { | ||
| return this.lottoWinnings.stream() | ||
| .filter(lottoWinning::isEqual) | ||
| .count(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class Lottos { | ||
|
|
||
| private final List<Lotto> lottos; | ||
|
|
||
| private Lottos(List<Lotto> lottos) { | ||
| this.lottos = lottos; | ||
| } | ||
|
|
||
| public static Lottos create(List<Lotto> lottos) { | ||
| return new Lottos(lottos); | ||
| } | ||
|
|
||
| public List<Lotto> toList() { | ||
| return List.copyOf(this.lottos); | ||
| } | ||
|
Comment on lines
+19
to
+21
Member
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.
제삼자가 봤을 때 많이 혼란스러운 네이밍은 변경해봐도 좋을 것 같습니다 :D (지난 번에도 비슷한 리뷰를 남긴 것으로 기억합니다!)
Author
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. Lottos는 Lotto의 일급 컬렉션으로 사용했던거라 일급 컬랙션을 List로 변환한다 라는 느낌에서 썼던건데 |
||
|
|
||
| public List<LottoWinning> findLottoWinnings(WinningNumbers winningNumbers, BonusNumber bonusNumber) { | ||
| return this.lottos.stream() | ||
| .map(lotto -> lotto.findLottoWinning(winningNumbers, bonusNumber)) | ||
| .collect(Collectors.toList()); | ||
| } | ||
|
|
||
| public Lottos addLottos(Lottos lottos) { | ||
| List<Lotto> copyLottos = new ArrayList<>(List.copyOf(this.lottos)); | ||
| copyLottos.addAll(lottos.toList()); | ||
|
|
||
| return new Lottos(copyLottos); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package domain; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.IntStream; | ||
|
|
||
| import static constant.LottoConstants.*; | ||
|
|
||
| public class LottosGenerator { | ||
|
|
||
| private static final List<Integer> totalLottoNumbers = | ||
| IntStream.range(MIN_LOTTO_NUMBER_INCLUSIVE, MAX_LOTTO_NUMBER_INCLUSIVE) | ||
| .boxed() | ||
| .collect(Collectors.toList()); | ||
|
|
||
| private final PurchaseCount purchaseCount; | ||
|
|
||
| public LottosGenerator(PurchaseCount purchaseCount) { | ||
| this.purchaseCount = purchaseCount; | ||
| } | ||
|
|
||
| public Lottos generate() { | ||
| return Lottos.create( | ||
| IntStream.range(0, this.purchaseCount.fetchPurchaseCount()) | ||
| .mapToObj(count -> this.generateLotto()) | ||
| .collect(Collectors.toList()) | ||
| ); | ||
| } | ||
|
|
||
| private Lotto generateLotto() { | ||
| Collections.shuffle(totalLottoNumbers); | ||
| List<Integer> lottoNumbers = totalLottoNumbers.subList(0, LOTTO_NUMBERS_SIZE); | ||
| Collections.sort(lottoNumbers); | ||
|
|
||
| return Lotto.create(List.copyOf(totalLottoNumbers.subList(0, LOTTO_NUMBERS_SIZE))); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
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.
이 클래스에서 inputView, outputView를 필드로 관리하지 않은 이유가 궁금해요!
외부에서 의존성을 주입받지 않고 메서드 내부에서 인스턴스를 생성할 때 어떤 문제가 있을지 같이 고민해보시죠~!
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.
외부에서 주입받는게 더 좋을 것 같아요!