Conversation
- 구입 금액 입력 - 당첨 번호 입력 - 보너스볼 입력
| 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)); |
There was a problem hiding this comment.
이 메서드는 너무 길고 가독성이 낮아 무엇을 하는지 파악하기가 매우 어렵습니다.🤯 (버그를 유발하기 쉬운구조입니다.) 적절한 단위로 묶어서 분리하고, 이름을 붙여서 의도를 드러내보면 좋을 것 같아요!
| public class LottoSimulator { | ||
|
|
||
| public void run() { | ||
| InputView inputView = new ConsoleInputView(); | ||
| OutputView outputView = new ConsoleOutputView(); |
There was a problem hiding this comment.
이 클래스에서 inputView, outputView를 필드로 관리하지 않은 이유가 궁금해요!
외부에서 의존성을 주입받지 않고 메서드 내부에서 인스턴스를 생성할 때 어떤 문제가 있을지 같이 고민해보시죠~!
| public static AutoPurchaseCount create(int purchaseCount) { | ||
| return new AutoPurchaseCount(purchaseCount); | ||
| } |
There was a problem hiding this comment.
이 팩토리 메서드는 생성자를 대신 호출하는 것 외에 별다른 로직이 없네요.
생성자 대신 팩토리 메서드를 쓴 이유가 궁금합니다. (특별히 팩토리 메서드를 사용한 의도를 알기 어렵습니다!)
There was a problem hiding this comment.
도메인객체를 단순히 생성(create)한다는 가독성을 위해 그렇게 했는데 단순 생성자를 사용하는 것으로도 고려해보겠습니다.
| private BonusNumber(int bonusNumber) { | ||
| this.bonusNumber = bonusNumber; | ||
| } | ||
|
|
||
| public static BonusNumber create(int bonusNumber) { | ||
| return new BonusNumber(bonusNumber); | ||
| } |
There was a problem hiding this comment.
어떤 정수도 범위에 상관없이 보너스 번호가 될 수 있는 구조입니다!
(이런 클래스를 미성숙한 클래스라고 하는데, 같이 공부해보시죠~!)
There was a problem hiding this comment.
validation하는 걸 놓친 부분이네요!
아니면 LottoNumber 클래스를 만들고 거기서 validation을 한 후에 위 클래스에서 상속받아야 겠어요
| public static LottoWinning findLottoWinning(long matchWinningNumberCount, long matchBonusNumberCount) { | ||
| return Arrays.stream(LottoWinning.values()) | ||
| .filter(lottoWinning -> lottoWinning.isMatchLottoWinning(matchWinningNumberCount, matchBonusNumberCount)) | ||
| .findAny() | ||
| .orElseGet(() -> ELSE_PLACE); | ||
| } |
There was a problem hiding this comment.
Optional의 orElseGet과 orElse의 차이를 학습해보시면 좋을 것 같아요!
(orElseGet의 구현이 어떻게 되어있는지 직접 보시면 더 좋습니다!)
| public static LottoWinning findLottoWinning(long matchWinningNumberCount, long matchBonusNumberCount) { | ||
| return Arrays.stream(LottoWinning.values()) | ||
| .filter(lottoWinning -> lottoWinning.isMatchLottoWinning(matchWinningNumberCount, matchBonusNumberCount)) |
There was a problem hiding this comment.
findLottoWinning을 호출할 때, 실수로 순서를 바꿔서 넣을 가능성이 있는 구조입니다!
위 같은 실수를 방지하기 위해서는 어떻게 설계해야 좋을지 같이 공부해보시죠!
There was a problem hiding this comment.
저 두 값을 위해 class를 생성해야 되나 생각이 들기도 하네요
There was a problem hiding this comment.
실수하기 쉬운 구조에서, 실수하기 매우 어려운 구조로 변경해나가는 것의 비용이라고 치면 가성비 괜찮다고 생각해요~! 🌝
객체지향 생활체조 원칙에도 나와있는 내용인데, 같이 공부해보시죠~!
| public long fetchWinningAmount() { | ||
| return this.winningAmount; | ||
| } | ||
|
|
||
| public long fetchMatchWinningNumberCount() { | ||
| return this.matchWinningNumberCount; | ||
| } |
There was a problem hiding this comment.
단순 getter 인데 fetch라는 prefix를 쓰신 이유가 궁금합니다 :)
There was a problem hiding this comment.
음.. 이 부분은 어떤 로직이 있다면 fetch를 쓰고 단순 데이터 조회면 getter를 쓰는 방향을 수정해봐야겠네요
| public List<Lotto> toList() { | ||
| return List.copyOf(this.lottos); | ||
| } |
There was a problem hiding this comment.
toList() 네이밍을 보고 다소 혼란스러웠습니다.
- Lottos를 list화 한다는데, 실제로는
List<Lottos>가 아니라List<Lotto>를 리턴하고 있어요. - toXXX 네이밍을 보아 뭔가 변환 작업을 이루어질 것으로 기대했는데 실제로는 getter입니다.
제삼자가 봤을 때 많이 혼란스러운 네이밍은 변경해봐도 좋을 것 같습니다 :D
(지난 번에도 비슷한 리뷰를 남긴 것으로 기억합니다!)
There was a problem hiding this comment.
Lottos는 Lotto의 일급 컬렉션으로 사용했던거라 일급 컬랙션을 List로 변환한다 라는 느낌에서 썼던건데
혼란이 있다면 네이밍을 고민해봐야겠네요
| public class LottosOutput { | ||
|
|
||
| private final List<LottoOutput> lottoOutputs; |
There was a problem hiding this comment.
LottoOutput의 리스트를 포함하는 값 객체이므로 클래스명도 LottoOutputs가 적합해보입니다!
| 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, "보너스 번호가 당첨 번호에 포함되어있으면 안됩니다."); | ||
| } | ||
| } |
There was a problem hiding this comment.
보너스 번호가 당첨 번호에 포함되면 안 된다는 validation이 LottosWinningCalculator 객체를 만드는 시점에서 이루어지도록 하신 이유가 궁금합니다! (요 validation이 LottosWinningCalculator에 있어야 할까요?)
There was a problem hiding this comment.
사실 이 validation의 위치가 애매했습니다.
BonusNumber에 있어야 할 validation이지만 그럼 BonusNumber를 생성할때 WinningNumbers를 넘겨줘야 하는 부분이 맘에 안들었어서요.
사용하지 않는데 validation만을 위해 넘겨줘야해서요. 그냥 BonusNumber를 생성할때 넘겨줘도 괜찮았나 생각이드네요
There was a problem hiding this comment.
사용하지 않는데 validation만을 위해 넘겨줘야해서요.
성숙한 인스턴스를 만들기 위해 validation을 위한 파라미터라면 '사용'의 범주로 봐도 괜찮을 것 같아요 :)
혹은 당첨 번호와 보너스 번호를 모두 입력받았을 때 그 즉시 validation 하는 것도 한 가지 방법이겠구요.
java-lotto
비즈니스 요구 사항
구현 기능 목록