-
Notifications
You must be signed in to change notification settings - Fork 355
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
[Step3] 로또(2등) 구현 #1084
[Step3] 로또(2등) 구현 #1084
Changes from all commits
3bac9d0
7699cd2
64b4a78
2e35465
8ca43a7
97f69a1
fd8bfc2
094c279
82259ca
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,28 @@ | ||
package lotto | ||
|
||
import lotto.const.LottoConst | ||
import lotto.domain.Lotto | ||
import lotto.domain.LottoNumber | ||
import lotto.domain.WinningLotto | ||
import lotto.util.NumberGenerator | ||
import lotto.util.RandomNumberGenerator | ||
|
||
class LottoCreator(private val numberGenerator: NumberGenerator = RandomNumberGenerator()) { | ||
fun createLottos(count: Int): List<Lotto> { | ||
return List(count) { createSingleLotto() } | ||
} | ||
|
||
fun createWinningLotto( | ||
winningNumbers: Set<Int>, | ||
bonusNumber: Int, | ||
): WinningLotto { | ||
val winningLotto = Lotto(winningNumbers.map { LottoNumber(it) }.toSet()) | ||
return WinningLotto(winningLotto, LottoConst.getLottoNumber(bonusNumber)) | ||
} | ||
|
||
private fun createSingleLotto(): Lotto { | ||
val randomNumbers = numberGenerator.generate() | ||
val lotto = Lotto(randomNumbers.map { LottoConst.LOTTO_NUMBERS[it - 1] }.toSet()) | ||
return lotto | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package lotto | ||
|
||
import lotto.const.LottoConst.UNIT_OF_AMOUNT | ||
import lotto.domain.Order | ||
|
||
class LottoShop(private val lottoCreator: LottoCreator) { | ||
fun makeOrder(amount: Int): Order { | ||
validateAmountIsPositive(amount) | ||
val lottoCounts = calculateLottoCounts(amount) | ||
return Order(amount, lottoCreator.createLottos(lottoCounts)) | ||
} | ||
|
||
private fun validateAmountIsPositive(amount: Int) { | ||
require(amount > 0) { "로또 구매 금액은 음수이거나 0원일 수 없습니다. (현재 입력 금액: $amount)" } | ||
} | ||
|
||
private fun calculateLottoCounts(amount: Int): Int { | ||
require(amount % UNIT_OF_AMOUNT == 0) { "로또 구매 금액은 1000원 단위로 입력되어야 합니다. (현재 입력 금액: $amount)" } | ||
return amount / UNIT_OF_AMOUNT | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,24 @@ | ||
package lotto | ||
|
||
import lotto.view.InputView | ||
import lotto.view.ResultView | ||
|
||
fun main() { | ||
val lottoSystem = LottoSystem() | ||
val lottoCreator = LottoCreator() | ||
val lottoShop = LottoShop(lottoCreator) | ||
val winningLottoMatcher = WinningLottoMatcher() | ||
|
||
// 주문 생성 | ||
val amount = InputView.getAmount() | ||
val order = lottoSystem.createOrder(amount) | ||
val order = lottoShop.makeOrder(amount) | ||
ResultView.printCreatedLottos(order.lottos) | ||
|
||
// 당첨번호 및 보너스번호 생성 | ||
val winNumberInput = InputView.getWinNumberInput() | ||
val winNumbers = lottoSystem.createWinNumbers(winNumberInput) | ||
val bonusNumber = InputView.getBonusNumber() | ||
val winNumbers = lottoCreator.createWinningLotto(winNumberInput, bonusNumber) | ||
|
||
val result = lottoSystem.createWinningResult(order, winNumbers) | ||
// 결과 출력 | ||
val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) | ||
ResultView.printResult(result) | ||
} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package lotto | ||
|
||
import lotto.domain.Lotto | ||
import lotto.domain.LottoResult | ||
import lotto.domain.Order | ||
import lotto.domain.Rank | ||
import lotto.domain.WinningLotto | ||
import lotto.domain.WinningResult | ||
|
||
class WinningLottoMatcher { | ||
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. WinningLottoMatcher의 책임은 무엇일까요? WinningLotto를 파라미터로 받아서 WinningLotto의 함수를 실행시켜주고 있네요 추가로 결과를 확인하기보단 결과를 가공해주는 로직이 더 많은건 아닐까요 :) |
||
fun checkAndGetResult( | ||
order: Order, | ||
winningLotto: WinningLotto, | ||
): WinningResult { | ||
val winningMatchCounts = aggregateLottoResult(order.lottos, winningLotto) | ||
return WinningResult(winningMatchCounts, order.amount) | ||
} | ||
|
||
private fun aggregateLottoResult( | ||
lottos: List<Lotto>, | ||
winningLotto: WinningLotto, | ||
): List<LottoResult> { | ||
val result = groupByRank(lottos, winningLotto) | ||
return result | ||
.map { (rank, count) -> LottoResult(count, rank) } | ||
.sortedBy { it.rank.prizeAmount } | ||
} | ||
|
||
private fun groupByRank( | ||
lottos: List<Lotto>, | ||
winningLotto: WinningLotto, | ||
): Map<Rank, Int> { | ||
val result = Rank.entries.filter { it !== Rank.MISS }.associateWith { 0 } | ||
val rankWithMatchCounts = | ||
lottos.map { winningLotto.matchLotto(it) } | ||
.filter { it !== Rank.MISS } | ||
.groupBy { it } | ||
.mapValues { (_, values) -> values.size } | ||
return result.mapValues { (rank, count) -> rankWithMatchCounts[rank] ?: count } | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package lotto.const | ||
|
||
import lotto.domain.LottoNumber | ||
|
||
object LottoConst { | ||
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.
저는 개인적으로 Const같은 상수 집합을 만드는것을 선호하지는 않아요! LottoConst라는 상수집합을 만들기보단, 상수값은 보통 책임있는곳에서 정의하는 편 입니다! |
||
const val UNIT_OF_AMOUNT = 1000 | ||
val LOTTO_NUMBERS = IntRange(1, 45).map { LottoNumber(it) }.toList() | ||
|
||
fun getLottoNumber(number: Int): LottoNumber { | ||
return requireNotNull(LOTTO_NUMBERS.find { it == LottoNumber(number) }) { "일치하는 번호의 로또번호가 존재하지 않습니다." } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package lotto.domain | ||
|
||
data class LottoResult( | ||
val totalCount: Int, | ||
val rank: Rank, | ||
) { | ||
Comment on lines
+3
to
+6
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. 로또의 결과보다는 로또의 통계를 나타내는건 아닐까요 ?:) |
||
fun getTotalPrizeMoney(): Int { | ||
return this.rank.prizeAmount * totalCount | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package lotto.domain | ||
|
||
import lotto.const.LottoConst.UNIT_OF_AMOUNT | ||
|
||
data class Order( | ||
val amount: Int, | ||
val lottos: List<Lotto>, | ||
) { | ||
init { | ||
validateLottoCounts() | ||
} | ||
|
||
private fun validateLottoCounts() { | ||
val count = amount / UNIT_OF_AMOUNT | ||
require(lottos.size == count) { "구매한 금액과 로또의 수량이 일치하지 않습니다." } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package lotto.domain | ||
|
||
enum class Rank( | ||
val matchCount: Int, | ||
val prizeAmount: Int, | ||
private val match: (Int, Boolean) -> Boolean, | ||
) { | ||
FIRST(6, 2_000_000_000, { count, _ -> count == 6 }), | ||
SECOND(5, 30_000_000, { count, isBonusMatch -> count == 5 && isBonusMatch }), | ||
THIRD(5, 1_500_000, { count, _ -> count == 5 }), | ||
FOURTH(4, 50_000, { count, _ -> count == 4 }), | ||
FIFTH(3, 5_000, { count, _ -> count == 3 }), | ||
MISS(0, 0, { count, _ -> count < 3 }), ; | ||
|
||
companion object { | ||
fun findByMatchCount( | ||
matchCount: Int, | ||
isBonusMatch: Boolean = false, | ||
): Rank { | ||
return entries.firstOrNull { it.match(matchCount, isBonusMatch) } | ||
?: throw RuntimeException("일치하는 숫자의 개수에 해당하는 상품이 존재하지 않습니다.") | ||
Comment on lines
+20
to
+21
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. 에러를 던지는것보다 null 이나 MISS 를 리턴함으로써 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. 코틀린에서는 Checked Exception를 강제하지 않는 등의 이유로 코틀린에서는 nullable인 경우에도 |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package lotto.domain | ||
|
||
class WinningLotto( | ||
val winningNumbers: Lotto, | ||
val bonusNumber: LottoNumber, | ||
) { | ||
Comment on lines
+3
to
+6
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. bonusNumber와 winningNumbers 중첩되는 상황도 고려하면 좋을거같아요 :) |
||
fun matchLotto(targetLotto: Lotto): Rank { | ||
return Rank.findByMatchCount( | ||
countMatchingNumbers(targetLotto), | ||
matchBonusNumber(targetLotto), | ||
) | ||
} | ||
|
||
fun countMatchingNumbers(targetLotto: Lotto): Int { | ||
return targetLotto.numbers.count { it in winningNumbers.numbers } | ||
} | ||
|
||
fun matchBonusNumber(targetLotto: Lotto): Boolean { | ||
return targetLotto.numbers.contains(bonusNumber) | ||
} | ||
Comment on lines
+14
to
+20
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으로 하면 어떨까요 :) |
||
} |
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.
미리생성한 로또넘버풀을 활용하면 어떨까요?
shuffled, take 등의 키워드를 활용해보아요!