Skip to content

Conversation

@kilian-develop
Copy link

java-lotto

비즈니스 요구 사항

  • 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다.
  • 로또 1장의 가격은 1000원이다.
  • 로또 구입 금액은 0보다 커야 한다.
  • 로또는 1개이상 구매해야한다.
  • 지난 주 당첨 번호는 "1, 2, 3, 4, 5, 6" 같은 형식이어야 한다.
  • 보너스 번호는 당첨 번호에 포함되면 안된다.

구현 기능 목록

  • 구입 금액 사용자 입력을 받는다.
  • 수동으로 구매할 로또 수를 입력한다.
  • 수동으로 구매할 번호를 입력한다.
  • 구입 금액에 맞춰 로또를 구입 갯수를 계산한다.
  • 구입한 로또의 갯수를 출력한다.
  • 구입한 로또의 목록을 생성한다.
  • 구입한 로또의 목록을 출력한다.
  • 지난 주 당첨 번호를 입력받는다.
  • 보너스 볼을 입력 받는다.
  • 당첨 결과를 판단한다.
  • 당첨 결과를 출력한다.

@kilian-develop kilian-develop requested review from haero77 and sc0116 May 19, 2024 10:16
@kilian-develop kilian-develop self-assigned this May 19, 2024
Copy link
Member

@haero77 haero77 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

킬리안 역시 제일 먼저 제출해주셨군요 👍
간단히 리뷰 남겨봤습니다 :D

지난 번과 비슷한 코멘트가 있을 수 있는데, 킬리안의 코드 작성에 대한 근거를 더 명확하게 세울 수 있도록 재차 확인하고 싶은 것으로 이해해주시면 고맙겠습니다.(그 사이에 새로운 근거/의도가 추가되었을지 또 모르죠..!)

Comment on lines +15 to +27
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));
Copy link
Member

@haero77 haero77 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 메서드는 너무 길고 가독성이 낮아 무엇을 하는지 파악하기가 매우 어렵습니다.🤯 (버그를 유발하기 쉬운구조입니다.) 적절한 단위로 묶어서 분리하고, 이름을 붙여서 의도를 드러내보면 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 분리해봐야겠어요

Comment on lines +13 to +17
public class LottoSimulator {

public void run() {
InputView inputView = new ConsoleInputView();
OutputView outputView = new ConsoleOutputView();
Copy link
Member

@haero77 haero77 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 클래스에서 inputView, outputView를 필드로 관리하지 않은 이유가 궁금해요!
외부에서 의존성을 주입받지 않고 메서드 내부에서 인스턴스를 생성할 때 어떤 문제가 있을지 같이 고민해보시죠~!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

외부에서 주입받는게 더 좋을 것 같아요!

Comment on lines +8 to +10
public static AutoPurchaseCount create(int purchaseCount) {
return new AutoPurchaseCount(purchaseCount);
}
Copy link
Member

@haero77 haero77 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 팩토리 메서드는 생성자를 대신 호출하는 것 외에 별다른 로직이 없네요.
생성자 대신 팩토리 메서드를 쓴 이유가 궁금합니다. (특별히 팩토리 메서드를 사용한 의도를 알기 어렵습니다!)

Copy link
Author

@kilian-develop kilian-develop May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인객체를 단순히 생성(create)한다는 가독성을 위해 그렇게 했는데 단순 생성자를 사용하는 것으로도 고려해보겠습니다.

Comment on lines +7 to +13
private BonusNumber(int bonusNumber) {
this.bonusNumber = bonusNumber;
}

public static BonusNumber create(int bonusNumber) {
return new BonusNumber(bonusNumber);
}
Copy link
Member

@haero77 haero77 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 정수도 범위에 상관없이 보너스 번호가 될 수 있는 구조입니다!
(이런 클래스를 미성숙한 클래스라고 하는데, 같이 공부해보시죠~!)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validation하는 걸 놓친 부분이네요!
아니면 LottoNumber 클래스를 만들고 거기서 validation을 한 후에 위 클래스에서 상속받아야 겠어요

Comment on lines +25 to +30
public static LottoWinning findLottoWinning(long matchWinningNumberCount, long matchBonusNumberCount) {
return Arrays.stream(LottoWinning.values())
.filter(lottoWinning -> lottoWinning.isMatchLottoWinning(matchWinningNumberCount, matchBonusNumberCount))
.findAny()
.orElseGet(() -> ELSE_PLACE);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional의 orElseGetorElse의 차이를 학습해보시면 좋을 것 같아요!
(orElseGet의 구현이 어떻게 되어있는지 직접 보시면 더 좋습니다!)

Comment on lines +25 to +27
public static LottoWinning findLottoWinning(long matchWinningNumberCount, long matchBonusNumberCount) {
return Arrays.stream(LottoWinning.values())
.filter(lottoWinning -> lottoWinning.isMatchLottoWinning(matchWinningNumberCount, matchBonusNumberCount))
Copy link
Member

@haero77 haero77 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findLottoWinning을 호출할 때, 실수로 순서를 바꿔서 넣을 가능성이 있는 구조입니다!
위 같은 실수를 방지하기 위해서는 어떻게 설계해야 좋을지 같이 공부해보시죠!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저 두 값을 위해 class를 생성해야 되나 생각이 들기도 하네요

Copy link
Member

@haero77 haero77 May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실수하기 쉬운 구조에서, 실수하기 매우 어려운 구조로 변경해나가는 것의 비용이라고 치면 가성비 괜찮다고 생각해요~! 🌝

객체지향 생활체조 원칙에도 나와있는 내용인데, 같이 공부해보시죠~!

Comment on lines +44 to +50
public long fetchWinningAmount() {
return this.winningAmount;
}

public long fetchMatchWinningNumberCount() {
return this.matchWinningNumberCount;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

단순 getter 인데 fetch라는 prefix를 쓰신 이유가 궁금합니다 :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음.. 이 부분은 어떤 로직이 있다면 fetch를 쓰고 단순 데이터 조회면 getter를 쓰는 방향을 수정해봐야겠네요

Comment on lines +19 to +21
public List<Lotto> toList() {
return List.copyOf(this.lottos);
}
Copy link
Member

@haero77 haero77 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toList() 네이밍을 보고 다소 혼란스러웠습니다.

  1. Lottos를 list화 한다는데, 실제로는 List<Lottos>가 아니라 List<Lotto>를 리턴하고 있어요.
  2. toXXX 네이밍을 보아 뭔가 변환 작업을 이루어질 것으로 기대했는데 실제로는 getter입니다.

제삼자가 봤을 때 많이 혼란스러운 네이밍은 변경해봐도 좋을 것 같습니다 :D

(지난 번에도 비슷한 리뷰를 남긴 것으로 기억합니다!)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lottos는 Lotto의 일급 컬렉션으로 사용했던거라 일급 컬랙션을 List로 변환한다 라는 느낌에서 썼던건데
혼란이 있다면 네이밍을 고민해봐야겠네요

Comment on lines +8 to +10
public class LottosOutput {

private final List<LottoOutput> lottoOutputs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LottoOutput의 리스트를 포함하는 값 객체이므로 클래스명도 LottoOutputs가 적합해보입니다!

Comment on lines +15 to +26
public LottosWinningCalculator(Lottos lottos, WinningNumbers winningNumbers, BonusNumber bonusNumber) {
this.validateBonusNumberNotContainWinningNumber(winningNumbers, bonusNumber);
this.lottos = lottos;
this.winningNumbers = winningNumbers;
this.bonusNumber = bonusNumber;
}

private void validateBonusNumberNotContainWinningNumber(WinningNumbers winningNumbers, BonusNumber bonusNumber) {
if (winningNumbers.isContainNumber(bonusNumber.fetchBonusNumber())) {
throw new DomainValidationException(WINNING_NUMBERS_CONTAIN_BONUS_NUMBER, "보너스 번호가 당첨 번호에 포함되어있으면 안됩니다.");
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보너스 번호가 당첨 번호에 포함되면 안 된다는 validation이 LottosWinningCalculator 객체를 만드는 시점에서 이루어지도록 하신 이유가 궁금합니다! (요 validation이 LottosWinningCalculator에 있어야 할까요?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 이 validation의 위치가 애매했습니다.
BonusNumber에 있어야 할 validation이지만 그럼 BonusNumber를 생성할때 WinningNumbers를 넘겨줘야 하는 부분이 맘에 안들었어서요.
사용하지 않는데 validation만을 위해 넘겨줘야해서요. 그냥 BonusNumber를 생성할때 넘겨줘도 괜찮았나 생각이드네요

Copy link
Member

@haero77 haero77 May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하지 않는데 validation만을 위해 넘겨줘야해서요.

성숙한 인스턴스를 만들기 위해 validation을 위한 파라미터라면 '사용'의 범주로 봐도 괜찮을 것 같아요 :)
혹은 당첨 번호와 보너스 번호를 모두 입력받았을 때 그 즉시 validation 하는 것도 한 가지 방법이겠구요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants