-
Notifications
You must be signed in to change notification settings - Fork 76
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
[로또 2단계] 공백 미션 제출합니다. #131
base: junseo511
Are you sure you want to change the base?
Changes from all commits
df07921
e193ea5
955673a
4328937
869f89a
1722035
669939c
0e9399f
4c35351
7e9b21b
59495d9
ff31ee6
0ca6a79
7fc0f1f
204952e
7f544ed
9f39975
f13927f
0916e12
b0aaff3
50da4ca
abdd447
9dcf73f
581d5dc
6d6f0b3
b6d29d2
35a9c55
285fd4d
ae35853
f328b98
66557d3
0c0a3ad
52e52ae
5c7e75d
0161382
7811eeb
adb6bab
14c6328
f842ceb
bee65d8
ff9bfa2
36b8292
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,14 +1,12 @@ | ||
package lotto | ||
|
||
import lotto.controller.LottoController | ||
import lotto.service.LottoService | ||
import lotto.view.InputView | ||
import lotto.view.OutputView | ||
|
||
fun main() { | ||
val inputView = InputView() | ||
val outputView = OutputView() | ||
val lottoService = LottoService() | ||
|
||
LottoController(inputView, outputView, lottoService).run() | ||
LottoController(inputView, outputView).run() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,94 @@ | ||
package lotto.controller | ||
|
||
import lotto.model.Amount | ||
import lotto.model.AutoLottoMachine | ||
import lotto.model.LottoMarket | ||
import lotto.model.LottoMarket.Companion.EMPTY_LOTTO_QUANTITY | ||
import lotto.model.LottoProfitCalculator | ||
import lotto.model.LottoWallet | ||
import lotto.model.ManualLottoMachine | ||
import lotto.model.ProfitStatus | ||
import lotto.model.Rank | ||
import lotto.service.LottoService | ||
import lotto.model.WinningDiscriminator | ||
import lotto.view.InputView | ||
import lotto.view.OutputView | ||
|
||
class LottoController( | ||
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.
위 처럼 입력하면 어떻게 될까요? 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.
객체 생성의 순서를 조금 바꿔야 할 필요가 있을 것 같네요 🧐 |
||
private val inputView: InputView, | ||
private val outputView: OutputView, | ||
private val lottoService: LottoService, | ||
) { | ||
fun run() { | ||
val purchaseAmount = getPurchaseAmount() | ||
val manualLottoQuantity = getManualQuantity() | ||
|
||
val lottoMarket = purchaseLottos(purchaseAmount, manualLottoQuantity) | ||
val lottoWallet = storeLottos(lottoMarket, manualLottoQuantity) | ||
|
||
val winningDiscriminator = getWinningInfo() | ||
discriminateLottos(winningDiscriminator, lottoWallet, purchaseAmount) | ||
} | ||
|
||
private fun getPurchaseAmount(): Amount { | ||
outputView.printPurchaseAmountGuide() | ||
val purchaseAmount = inputView.readPurchaseAmount() | ||
val lottos = lottoService.getPurchaseLottos(purchaseAmount) | ||
return Amount(purchaseAmount) | ||
} | ||
|
||
outputView.printPurchaseLottoQuantity(lottos.lottos.size) | ||
lottos.lottos.forEach { lotto -> | ||
outputView.printLottoNumbers(lotto.numbers.map { it.number }) | ||
} | ||
private fun getManualQuantity(): Int { | ||
outputView.printManualLottoQuantityGuide() | ||
val manualLottoQuantity = inputView.readManualLottoQuantity() | ||
return manualLottoQuantity | ||
} | ||
|
||
outputView.printWinningNumbersGuide() | ||
val winningNumbers = inputView.readWinningNumbers() | ||
private fun purchaseLottos( | ||
purchaseAmount: Amount, | ||
manualLottoQuantity: Int, | ||
): LottoMarket { | ||
val autoLottoMachine = AutoLottoMachine() | ||
val manualLottoMachine = ManualLottoMachine() | ||
val lottoMarket = LottoMarket(purchaseAmount, manualLottoQuantity, manualLottoMachine, autoLottoMachine) | ||
outputView.printManualLottoNumbersGuide(manualLottoQuantity > EMPTY_LOTTO_QUANTITY) | ||
return lottoMarket | ||
} | ||
|
||
outputView.printBonusNumberGuide() | ||
val bonusNumber = inputView.readBonusNumber() | ||
private fun storeLottos( | ||
lottoMarket: LottoMarket, | ||
manualLottoQuantity: Int, | ||
): LottoWallet { | ||
val lottoWallet = LottoWallet() | ||
|
||
val lottoWinningResult = lottoService.getLottosDiscriminateResult(lottos, winningNumbers, bonusNumber) | ||
lottoMarket.buy( | ||
List(manualLottoQuantity) { inputView.readLottoNumbers() }, | ||
lottoWallet, | ||
) | ||
|
||
outputView.printPurchaseLottoQuantity(manualLottoQuantity, lottoMarket.autoLottoQuantity) | ||
outputView.printLotto(lottoWallet.lottos) | ||
return lottoWallet | ||
} | ||
|
||
private fun discriminateLottos( | ||
winningDiscriminator: WinningDiscriminator, | ||
lottoWallet: LottoWallet, | ||
purchaseAmount: Amount, | ||
) { | ||
val winningResult = winningDiscriminator.getResult(lottoWallet) | ||
outputView.printWinningResultTitle() | ||
lottoWinningResult.forEach { (rank, count) -> | ||
if (rank == Rank.MISS) return@forEach | ||
|
||
outputView.printWinningResult( | ||
requiredMatch = rank.countOfMatch, | ||
profit = rank.winningMoney, | ||
matchBonus = rank == Rank.SECOND, | ||
countOfMatch = count, | ||
) | ||
} | ||
|
||
val profitRate = lottoService.getProfitRate(lottoWinningResult, purchaseAmount) | ||
outputView.printWinningLottoResult(winningResult) | ||
|
||
val lottoProfitCalculator = LottoProfitCalculator() | ||
val profitRate = lottoProfitCalculator.getProfitRate(winningResult, purchaseAmount) | ||
val profitStatus = ProfitStatus.from(profitRate) | ||
outputView.printProfitRate(profitRate, profitStatus) | ||
} | ||
|
||
private fun getWinningInfo(): WinningDiscriminator { | ||
outputView.printWinningNumbersGuide() | ||
val winningNumbers = inputView.readLottoNumbers() | ||
|
||
outputView.printBonusNumberGuide() | ||
val bonusNumber = inputView.readBonusNumber() | ||
|
||
outputView.printProfitRate(profitRate, profitStatus.krDescription) | ||
val winningDiscriminator = WinningDiscriminator(winningNumbers, bonusNumber) | ||
return winningDiscriminator | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,30 @@ | ||
package lotto.model | ||
|
||
class LottoCashier( | ||
val amount: Int, | ||
@JvmInline | ||
value class Amount( | ||
val value: Int, | ||
Comment on lines
+3
to
+5
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. 클래스를 만들었으니 테스트도 추가하면 좋겠네요! 😊 |
||
) { | ||
init { | ||
validateAmountMinimumRange() | ||
validateAmountUnit() | ||
} | ||
|
||
fun isAffordable(manualQuantity: Int): Boolean = value - manualQuantity * LOTTO_EACH_AMOUNT >= 0 | ||
|
||
fun getAutoLottoQuantity(manualQuantity: Int): Int = value / LOTTO_EACH_AMOUNT - manualQuantity | ||
|
||
private fun validateAmountMinimumRange() { | ||
require(amount > LOTTO_MIN_AMOUNT) { | ||
"[ERROR] ${LOTTO_MIN_AMOUNT}원 이상의 금액으로 입력해 주세요. 입력값: $amount" | ||
require(value > LOTTO_MIN_AMOUNT) { | ||
"[ERROR] ${LOTTO_MIN_AMOUNT}원 이상의 금액으로 입력해 주세요. 입력값: $value" | ||
} | ||
} | ||
|
||
private fun validateAmountUnit() { | ||
require(amount % LOTTO_EACH_AMOUNT == 0) { | ||
"[ERROR] ${LOTTO_EACH_AMOUNT}원 단위의 금액으로 입력해 주세요. 입력값: $amount" | ||
require(value % LOTTO_EACH_AMOUNT == 0) { | ||
"[ERROR] ${LOTTO_EACH_AMOUNT}원 단위의 금액으로 입력해 주세요. 입력값: $value" | ||
} | ||
} | ||
|
||
fun getPurchaseQuantity(): Int = amount / LOTTO_EACH_AMOUNT | ||
|
||
companion object { | ||
private const val LOTTO_MIN_AMOUNT = 0 | ||
private const val LOTTO_EACH_AMOUNT = 1000 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package lotto.model | ||
|
||
import lotto.model.Lotto.Companion.LOTTO_NUMBER_SIZE | ||
import lotto.model.LottoNumber.Companion.cachedLottoNumbers | ||
|
||
class AutoLottoMachine : LottoMachine { | ||
override fun generate( | ||
manualTicket: List<List<Int>>, | ||
quantity: Int, | ||
): List<Lotto> = List(quantity) { Lotto(getAutoNumbers()) } | ||
|
||
private fun getAutoNumbers(): List<LottoNumber> = | ||
cachedLottoNumbers | ||
.shuffled() | ||
.take(LOTTO_NUMBER_SIZE) | ||
.sortedBy { number -> number.value } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,29 @@ | ||
package lotto.model | ||
|
||
class Lotto private constructor( | ||
class Lotto( | ||
val numbers: List<LottoNumber>, | ||
) { | ||
init { | ||
validateLottoNumbersCount(numbers) | ||
validateLottoNumbersDuplicate(numbers) | ||
validateLottoNumbersCount() | ||
validateLottoNumbersDuplicate() | ||
} | ||
|
||
private fun validateLottoNumbersCount(numbers: List<LottoNumber>) { | ||
fun countMatchNumbers(other: Lotto): Int = numbers.count { number -> number in other.numbers } | ||
|
||
fun contains(number: LottoNumber): Boolean = numbers.contains(number) | ||
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. 네이밍 👍 |
||
|
||
private fun validateLottoNumbersCount() { | ||
require(numbers.size == LOTTO_NUMBER_SIZE) { | ||
"[ERROR] 로또는 ${LOTTO_NUMBER_SIZE}개의 번호만 가질 수 있습니다." | ||
} | ||
} | ||
|
||
private fun validateLottoNumbersDuplicate(lottoNumbers: List<LottoNumber>) { | ||
val uniqueNumbers = lottoNumbers.map { it.number }.toSet() | ||
require(uniqueNumbers.size == lottoNumbers.size) { | ||
private fun validateLottoNumbersDuplicate() { | ||
require(numbers.distinct().size == numbers.size) { | ||
"[ERROR] 로또 번호는 중복될 수 없습니다." | ||
} | ||
} | ||
|
||
fun isHaveBonus(number: LottoNumber): Boolean = numbers.contains(number) | ||
|
||
companion object { | ||
const val LOTTO_NUMBER_SIZE = 6 | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,8 @@ | ||
package lotto.model | ||
|
||
import lotto.model.Lotto.Companion.LOTTO_NUMBER_SIZE | ||
import lotto.model.LottoNumber.Companion.ALL_LOTTO_NUMBERS | ||
|
||
class LottoMachine { | ||
fun getLottos(lottoQuantity: Int): Lottos { | ||
val lottos = List(lottoQuantity) { Lotto.from(getLottoNumbers()) } | ||
return Lottos(lottos) | ||
} | ||
|
||
private fun getLottoNumbers(): List<Int> = | ||
ALL_LOTTO_NUMBERS | ||
.shuffled() | ||
.take(LOTTO_NUMBER_SIZE) | ||
.sorted() | ||
interface LottoMachine { | ||
fun generate( | ||
manualTicket: List<List<Int>> = emptyList(), | ||
quantity: Int = manualTicket.size, | ||
): List<Lotto> | ||
Comment on lines
+3
to
+7
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.
디폴트 값을 넘기면 어떤 문제가 발생할 수 있을까요? 인터페이스로 추상화하는 이유가 무엇일까에 대해서 먼저 고민해봐면 좋을 것 같은데요. 공백이 작성해준 방법은 만약 반자동 발매기가 생긴다면 어떻게 해야 할까요? 이 역시도 인터페이스의 수정이 일어나야 할까요? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package lotto.model | ||
|
||
class LottoMarket( | ||
private val amount: Amount, | ||
private val manualQuantity: Int, | ||
private val manualLottoMachine: ManualLottoMachine = ManualLottoMachine(), | ||
private val autoLottoMachine: AutoLottoMachine = AutoLottoMachine(), | ||
) { | ||
Comment on lines
+3
to
+8
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.
의존성을 주입하는 방법은 대표적으로 3가지가 있으니 한 번 찾아보시고, |
||
val autoLottoQuantity: Int = amount.getAutoLottoQuantity(manualQuantity) | ||
|
||
init { | ||
validateManualLottoQuantity() | ||
} | ||
|
||
fun buy( | ||
manualNumbers: List<List<Int>> = emptyList(), | ||
lottoWallet: LottoWallet, | ||
) { | ||
Comment on lines
+15
to
+18
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.
|
||
lottoWallet.addAll( | ||
when (manualNumbers.isNotEmpty()) { | ||
true -> manualLottoMachine.generate(manualTicket = manualNumbers) | ||
false -> autoLottoMachine.generate(quantity = autoLottoQuantity) | ||
}, | ||
) | ||
} | ||
|
||
private fun validateManualLottoQuantity() { | ||
require(amount.isAffordable(manualQuantity)) { | ||
"[ERROR] 낸 금액보다 많은 수동 로또를 살 수 없습니다. 금액: ${amount.value}, 수동 로또: ${manualQuantity}장" | ||
} | ||
} | ||
|
||
companion object { | ||
const val EMPTY_LOTTO_QUANTITY = 0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,19 @@ | ||
package lotto.model | ||
|
||
class LottoNumber private constructor( | ||
val number: Int, | ||
val value: Int, | ||
) { | ||
companion object { | ||
private const val LOTTO_NUMBER_MIN_RANGE = 1 | ||
private const val LOTTO_NUMBER_MAX_RANGE = 45 | ||
|
||
val ALL_LOTTO_NUMBERS = (LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE) | ||
private val CACHE = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) } | ||
private val CACHE_LOTTO_NUMBER = | ||
(LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE).associateWith { LottoNumber(it) } | ||
|
||
val cachedLottoNumbers: List<LottoNumber> by lazy { CACHE_LOTTO_NUMBER.values.toList() } | ||
|
||
fun from(number: Int): LottoNumber = | ||
CACHE[number] | ||
CACHE_LOTTO_NUMBER[number] | ||
?: throw IllegalArgumentException("[ERROR] 로또 번호의 범위는 $LOTTO_NUMBER_MIN_RANGE 이상 $LOTTO_NUMBER_MAX_RANGE 이하여야 합니다.") | ||
} | ||
} |
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.
위 처럼 입력하면 어떻게 될까요?
예상한 동작일까요? 🤔
추가로 요구사항 중 하나인
예외 처리를 통해 에러가 발생하지 않도록 한다.
에 대해서도 고민해보면 좋을 것 같아요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.
컨트롤러를 구현하다가 보면 객체 순서를 맞추다가 하나씩 자꾸 놓치는 것 같아요 🥲
이 부분은 Amount를 포장하면서 자연스럽게 해결이 되었네요 👍
36b8292
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.
리뷰를 읽고 ValidateResult라는 sealed class를 만들고‼️
Success<T>
로 반환값을 감싸서 만들어보려고 했는데,생각보다 많은 부분을 수정해야 하더라구요 🥲 이번 수정 기간 안에는 완성하지 못할 것 같아서 블랙잭에서 시도해보겠습니다
조만간 방과후에서 획득당한(?) 주제이기도 하네요 🤣
다음번엔 여유가 생길때 과제 힌트의 구현사항도 하나씩 뿌셔봐야겠어요 😇