-
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단계] 시아 미션 제출합니다. #135
base: leeyerin0210
Are you sure you want to change the base?
Changes from 1 commit
79bc0e4
133dce9
02cb6d7
29794c2
d9ec1a0
66b2a02
1907483
6a9e9d9
4ed8d62
1db3c57
4485eac
6dd8b3d
63bb963
e401cbd
d07db8c
422eaac
0050e23
e7f5664
8eb1895
8444009
0fd886a
fece2a8
0220a90
09eb61b
19253ff
33d22ea
00d4c70
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,12 @@ | ||
package lotto | ||
|
||
import lotto.controller.LottoController | ||
import lotto.view.InputView | ||
import lotto.view.OutputView | ||
|
||
fun main() { | ||
val inputView = InputView() | ||
val outputView = OutputView() | ||
val controller = LottoController(inputView, outputView) | ||
controller.run() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package lotto.controller | ||
|
||
import lotto.domain.model.Amount | ||
import lotto.domain.model.Lotto | ||
import lotto.domain.model.LottoCreationResult | ||
import lotto.domain.model.LottoNumber | ||
import lotto.domain.model.Rank | ||
import lotto.domain.model.WinningLotto | ||
import lotto.domain.model.WinningLottoCreationResult | ||
import lotto.domain.service.RankCalculator | ||
import lotto.domain.service.WinningListMaker | ||
import lotto.view.InputView | ||
import lotto.view.Message | ||
import lotto.view.OutputView | ||
|
||
class LottoController( | ||
val inputView: InputView, | ||
val outputView: OutputView, | ||
) { | ||
val amount = inputAmount() | ||
|
||
fun run() { | ||
val count = getManualCount() | ||
val manualLottoList = getLottoList(count) | ||
val autoLottoList = getAutoLotto(amount.getCount(LOTTO_PRIZE) - count) | ||
|
||
outputView.printPurchaseResult(manualLottoList, autoLottoList) | ||
|
||
val winningLotto = getWinningLotto() | ||
val ranks = WinningListMaker().calculateRanks(winningLotto, manualLottoList + autoLottoList) | ||
|
||
val sortedResults = sortResultsByOriginalRankOrder(ranks) | ||
val totalWinnings = RankCalculator().earningMoney(ranks) | ||
val earningRate = RankCalculator().calculateEarningRate(amount.money, totalWinnings) | ||
|
||
outputView.printResult(sortedResults, earningRate) | ||
} | ||
|
||
fun getAutoLotto(count: Int): List<Lotto> = List(count) { Lotto.createRandom() } | ||
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으로 열 필요가있을까요? 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. 19253ff |
||
|
||
fun getManualCount(): Int { | ||
val manualCount = inputView.getManualCount() | ||
return if (amount.getCount(LOTTO_PRIZE) >= manualCount) { | ||
manualCount | ||
} else { | ||
outputView.printErrorMessage(Message.errorCountExceeded()) | ||
getManualCount() | ||
} | ||
} | ||
|
||
fun getLottoList(count: Int): List<Lotto> { | ||
inputView.messageManualLotto() | ||
return List(count) { inputLotto() } | ||
} | ||
|
||
private fun inputLotto(): Lotto { | ||
when ( | ||
val result = | ||
Lotto.create( | ||
inputView | ||
.getManualLotto() | ||
.mapNotNull { LottoNumber.createOrNull(it) } | ||
.sortedBy { it.value }, | ||
) | ||
) { | ||
is LottoCreationResult.Success -> return result.lotto | ||
is LottoCreationResult.Failure.InvalidCount -> outputView.printErrorMessage(Message.errorInvalidLotto()) | ||
is LottoCreationResult.Failure.DuplicatedNumbers -> outputView.printErrorMessage(Message.errorInvalidLotto()) | ||
is LottoCreationResult.Failure.NotSorted -> outputView.printErrorMessage(Message.errorInvalidLotto()) | ||
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. 커스텀 예외를 만들었지만 결국 동일한 출력이 발생하는 거라면 오버엔지니어링으로 보이네요! runCatching {
val lotto = Lotto.valueOf(...)
}.getOrElse { ... } 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. 동일한 출력이 발생하지 않게, 출력 메세지를 세분화하였습니다. |
||
} | ||
return inputLotto() | ||
} | ||
|
||
private fun inputAmount(): Amount { | ||
val amount = Amount.createOrNull(inputView.getMoney()) | ||
return amount ?: run { | ||
outputView.printErrorMessage(Message.errorInvalidAmount()) | ||
inputAmount() | ||
} | ||
} | ||
|
||
private fun getWinningLotto(): WinningLotto { | ||
when ( | ||
val result = | ||
WinningLotto.create( | ||
inputView.getWinningLotto().mapNotNull { LottoNumber.createOrNull(it) }, | ||
LottoNumber.createOrNull(inputView.getBonusNumber()) ?: return getWinningLotto(), | ||
) | ||
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. 코드 깊이가 너무 들어갔어요. |
||
) { | ||
is WinningLottoCreationResult.Success -> return result.winningLotto | ||
is WinningLottoCreationResult.Failure.NumberSizeError -> outputView.printErrorMessage(Message.errorInvalidWinningNumbers()) | ||
is WinningLottoCreationResult.Failure.BonusNumberDuplicated -> outputView.printErrorMessage(Message.errorInvalidBonusNumber()) | ||
is WinningLottoCreationResult.Failure.DuplicatedNumbers -> outputView.printErrorMessage(Message.errorInvalidWinningNumbers()) | ||
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. result 타입을 그대로 뷰에 전달하면 뷰가 너무 도메인에 대한 세부 정보를 알게 되는 것이 아닌가..해서 이렇게 작성했습니다! |
||
} | ||
return getWinningLotto() | ||
} | ||
|
||
private fun sortResultsByOriginalRankOrder(ranks: List<Rank>): List<Pair<Rank, Int>> = | ||
Rank.entries | ||
.filter { it != Rank.MISS } | ||
.map { rank -> rank to ranks.count { it == rank } } | ||
.reversed() | ||
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. 33d22ea |
||
|
||
companion object { | ||
const val LOTTO_PRIZE = 1000 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,41 @@ | ||
package lotto.domain.model | ||
|
||
sealed class WinningLottoCreationResult { | ||
data class Success( | ||
val winningLotto: WinningLotto, | ||
) : WinningLottoCreationResult() | ||
|
||
sealed class Failure : WinningLottoCreationResult() { | ||
object BonusNumberDuplicated : Failure() | ||
|
||
object DuplicatedNumbers : Failure() | ||
|
||
object NumberSizeError : Failure() | ||
} | ||
} | ||
|
||
class WinningLotto private constructor( | ||
private val lottoNumbers: List<LottoNumber>, | ||
private val bonusNumber: LottoNumber, | ||
) { | ||
fun findRank(lotto: Lotto): Rank { | ||
val countOfMatch = | ||
lottoNumbers.intersect(lotto.numberList).size | ||
val countOfMatch = lottoNumbers.intersect(lotto.numberList).size | ||
val bonusMatched = lotto.numberList.contains(bonusNumber) | ||
return Rank.valueOf(countOfMatch, bonusMatched) | ||
} | ||
|
||
companion object { | ||
const val NUMBER_LIST_DUPLICATED = "[ERROR] 당첨 번호가 중복됩니다." | ||
const val BONUS_NUMBER_DUPLICATED = "[ERROR] 보너스 번호가 중복됩니다." | ||
|
||
fun valueOf( | ||
inputNumbers: List<LottoNumber>, | ||
bonusNumber: LottoNumber, | ||
): WinningLotto { | ||
require(!inputNumbers.contains(bonusNumber)) { BONUS_NUMBER_DUPLICATED } | ||
require(inputNumbers.distinctBy { it.value } == inputNumbers) { NUMBER_LIST_DUPLICATED } | ||
return WinningLotto(inputNumbers, bonusNumber) | ||
} | ||
const val WINNING_LOTTO_NUMBER_QUANTITY = 6 | ||
|
||
fun createOrNull( | ||
inputNumbers: List<LottoNumber>, | ||
fun create( | ||
numbers: List<LottoNumber>, | ||
bonusNumber: LottoNumber, | ||
): WinningLotto? { | ||
if (inputNumbers.contains(bonusNumber)) return null | ||
if (inputNumbers.distinctBy { it.value } != inputNumbers) return null | ||
return WinningLotto(inputNumbers, bonusNumber) | ||
} | ||
): WinningLottoCreationResult = | ||
when { | ||
numbers.size != WINNING_LOTTO_NUMBER_QUANTITY -> WinningLottoCreationResult.Failure.NumberSizeError | ||
numbers.distinctBy { it.value }.size != numbers.size -> WinningLottoCreationResult.Failure.DuplicatedNumbers | ||
numbers.contains(bonusNumber) -> WinningLottoCreationResult.Failure.BonusNumberDuplicated | ||
else -> WinningLottoCreationResult.Success(WinningLotto(numbers, bonusNumber)) | ||
} | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package lotto.view | ||
|
||
import lotto.view.InputMessage.ALERT_INPUT_LOTTO | ||
import lotto.view.InputMessage.BONUS_NUMBER_INPUT_MESSAGE | ||
import lotto.view.InputMessage.COUNT_INPUT_MESSAGE | ||
import lotto.view.InputMessage.ERROR_MESSAGE | ||
import lotto.view.InputMessage.MONEY_INPUT_MESSAGE | ||
import lotto.view.InputMessage.WINNING_LOTTO_INPUT_MESSAGE | ||
|
||
object InputMessage { | ||
const val MONEY_INPUT_MESSAGE = "구입금액을 입력해 주세요." | ||
const val WINNING_LOTTO_INPUT_MESSAGE = "\n지난 주 당첨 번호를 입력해 주세요." | ||
const val BONUS_NUMBER_INPUT_MESSAGE = "보너스 볼을 입력해 주세요." | ||
const val COUNT_INPUT_MESSAGE = "\n수동으로 구매할 로또 수를 입력해 주세요." | ||
const val ALERT_INPUT_LOTTO = "수동으로 구매할 번호를 입력해 주세요." | ||
const val ERROR_MESSAGE = "[ERROR] 입력이 올바르지 않습니다. 다시 입력해 주세요." | ||
} | ||
|
||
class InputView { | ||
fun getMoney(): Int = getValidSingleNumber(MONEY_INPUT_MESSAGE) | ||
|
||
fun getWinningLotto(): List<Int> = getValidMultipleNumbers(WINNING_LOTTO_INPUT_MESSAGE) | ||
|
||
fun getBonusNumber(): Int = getValidSingleNumber(BONUS_NUMBER_INPUT_MESSAGE) | ||
|
||
fun getManualCount(): Int = getValidSingleNumber(COUNT_INPUT_MESSAGE) | ||
|
||
fun messageManualLotto() = println(ALERT_INPUT_LOTTO) | ||
|
||
fun getManualLotto(): List<Int> = getValidMultipleNumbers(null) | ||
|
||
private fun getValidSingleNumber(message: String): Int { | ||
while (true) { | ||
println(message) | ||
val input = readln().trim() | ||
val number = input.toIntOrNull() | ||
|
||
if (number != null) return number | ||
println(ERROR_MESSAGE) | ||
} | ||
} | ||
|
||
private fun getValidMultipleNumbers(message: String?): List<Int> { | ||
while (true) { | ||
message?.let { println(it) } | ||
val input = readln().trim() | ||
val numbers = input.split(",").mapNotNull { it.trim().toIntOrNull() } | ||
|
||
if (numbers.isNotEmpty() && numbers.size == input.split(",").size) return numbers | ||
println(ERROR_MESSAGE) | ||
} | ||
} | ||
} |
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.
입출력 기능을 실행시키기 위한 컨트롤러도 포함되는 줄 알았습니다. 앞으로는 컨트롤러는 따로 커밋하도록 하겠습니다!