-
Notifications
You must be signed in to change notification settings - Fork 357
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
Step4 - 로또(수동) #997
base: sodp5
Are you sure you want to change the base?
Step4 - 로또(수동) #997
Changes from all commits
356d739
538f031
c476488
4fa3fdd
f085421
6fc44fc
93c0bc2
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,25 +1,43 @@ | ||
package lotto | ||
|
||
import lotto.domain.DefaultLottoGenerateStrategy | ||
import lotto.domain.AutoLottoNumbersGenerateStrategy | ||
import lotto.domain.Lotto | ||
import lotto.domain.LottoGenerator | ||
import lotto.domain.LottoNumber | ||
import lotto.domain.LottoStore | ||
import lotto.domain.Lottos | ||
import lotto.domain.ManualLottoNumbersGenerateStrategy | ||
import lotto.domain.WinningLotto | ||
import lotto.ui.InputView | ||
import lotto.ui.ResultView | ||
|
||
fun main() { | ||
val generator = LottoGenerator(DefaultLottoGenerateStrategy()) | ||
val store = LottoStore(generator) | ||
val store = LottoStore() | ||
|
||
val money = InputView.inputMoney() | ||
val boughtLottos = store.buyLottos(money) | ||
ResultView.showBoughtLottos(boughtLottos) | ||
val boughtManualLottoCounts = InputView.inputManualLottoCount() | ||
|
||
val winningLotto = WinningLotto(Lotto(InputView.inputWinningNumbers()), InputView.inputBonusNumber()) | ||
val manualLottosPrice = boughtManualLottoCounts * LottoStore.LOTTO_PRICE | ||
|
||
val checkedLottos = boughtLottos.matchAll(winningLotto) | ||
val returnRate = checkedLottos.totalReward() / money.toDouble() | ||
require(money >= manualLottosPrice) { | ||
"로또를 구매할 돈이 부족합니다." | ||
} | ||
|
||
ResultView.showResult(checkedLottos, returnRate) | ||
val manualLottos = List(boughtManualLottoCounts) { | ||
val lottoNumbers = InputView.inputManualLottoNumber() | ||
|
||
store.buyLotto(ManualLottoNumbersGenerateStrategy(lottoNumbers)) | ||
}.let { Lottos(it) } | ||
|
||
val autoLottos = store.buyLottos(money - manualLottosPrice, AutoLottoNumbersGenerateStrategy()) | ||
|
||
val boughtLottos = manualLottos + autoLottos | ||
|
||
ResultView.showBoughtLottos(boughtLottos, boughtManualLottoCounts) | ||
|
||
val winningLotto = WinningLotto(Lotto.of(InputView.inputWinningNumbers()), LottoNumber(InputView.inputBonusNumber())) | ||
val matchedLotto = boughtLottos.matchAll(winningLotto) | ||
|
||
val returnRate = matchedLotto.totalReward() / money.toDouble() | ||
|
||
ResultView.showResult(matchedLotto, returnRate) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package lotto.domain | ||
|
||
class AutoLottoNumbersGenerateStrategy : LottoNumbersGenerateStrategy { | ||
private val numberPool: IntRange = LottoNumber.MIN_NUMBER..LottoNumber.MAX_NUMBER | ||
override fun generate(): LottoNumbers { | ||
return numberPool | ||
.shuffled() | ||
.subList(0, Lotto.NUMBERS_COUNT) | ||
.sorted() | ||
.let { LottoNumbers.of(it) } | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,22 @@ | ||
package lotto.domain | ||
|
||
class Lotto( | ||
val numbers: List<Int>, | ||
val winning: LottoWinning = LottoWinning.Miss, | ||
val numbers: LottoNumbers, | ||
) { | ||
init { | ||
require(numbers.size == NUMBERS_COUNT) { | ||
"로또 번호는 항상 ${NUMBERS_COUNT}개 여야 합니다." | ||
} | ||
require(numbers.distinct().size == NUMBERS_COUNT) { | ||
"로또 번호는 중복이 없어야 합니다." | ||
} | ||
require(numbers.all { it in MIN_NUMBER..MAX_NUMBER }) { | ||
"로또 번호는 항상 ${MIN_NUMBER}에서 ${MAX_NUMBER}사이 값이어야 합니다." | ||
} | ||
require(numbers.isSortedNumber()) { | ||
"로또 번호는 항상 정렬되어야 합니다." | ||
} | ||
} | ||
|
||
fun match(winningLotto: WinningLotto): Lotto { | ||
val correctCount = winningLotto.lotto.numbers.count { numbers.contains(it) } | ||
fun match(winningLotto: WinningLotto): MatchedLotto { | ||
val correctCount = winningLotto.lotto.numbers.containsCount(numbers) | ||
val matchBonus = numbers.contains(winningLotto.bonusNumber) | ||
Comment on lines
+7
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. getter를 없애는 방법은 디미터의 법칙에 대해서 읽어보시면 좋을 것 같네요. |
||
|
||
return Lotto( | ||
numbers = numbers, | ||
winning = LottoWinning.of(correctCount, matchBonus) | ||
) | ||
} | ||
|
||
private fun List<Int>.isSortedNumber(): Boolean { | ||
return zipWithNext { a, b -> a <= b }.all { it } | ||
return MatchedLotto(this, LottoWinning.of(correctCount, matchBonus)) | ||
} | ||
|
||
companion object { | ||
const val NUMBERS_COUNT = 6 | ||
const val MIN_NUMBER = 1 | ||
const val MAX_NUMBER = 45 | ||
|
||
fun of(numbers: List<Int>): Lotto { | ||
val lottoNumbers = LottoNumbers(numbers.map { LottoNumber(it) }) | ||
|
||
return Lotto(lottoNumbers) | ||
} | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
package lotto.domain | ||
|
||
class LottoGenerator(private val lottoGenerateStrategy: LottoGenerateStrategy) { | ||
fun publish(): Lotto { | ||
return lottoGenerateStrategy.generate() | ||
class LottoGenerator { | ||
fun publish(lottoNumbersGenerateStrategy: LottoNumbersGenerateStrategy): Lotto { | ||
return Lotto(lottoNumbersGenerateStrategy.generate()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package lotto.domain | ||
|
||
@JvmInline | ||
value class LottoNumber(val value: Int) : Comparable<LottoNumber> { | ||
init { | ||
require(value in MIN_NUMBER..MAX_NUMBER) { | ||
"로또 번호는 항상 ${MIN_NUMBER}에서 ${MAX_NUMBER}사이 값이어야 합니다." | ||
} | ||
} | ||
|
||
override fun compareTo(other: LottoNumber): Int { | ||
return value.compareTo(other.value) | ||
} | ||
|
||
companion object { | ||
const val MIN_NUMBER = 1 | ||
const val MAX_NUMBER = 45 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package lotto.domain | ||
|
||
@JvmInline | ||
value class LottoNumbers(private val value: List<LottoNumber>) { | ||
|
||
init { | ||
require(value.size == Lotto.NUMBERS_COUNT) { | ||
"로또 번호는 항상 ${Lotto.NUMBERS_COUNT}개 여야 합니다." | ||
Comment on lines
+7
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. count 상수도 여기 위치하는게 맞지 않을까요? |
||
} | ||
require(!hasDuplicate()) { | ||
"로또 번호는 중복이 없어야 합니다." | ||
} | ||
require(isSorted()) { | ||
"로또 번호는 항상 정렬되어야 합니다." | ||
} | ||
} | ||
|
||
val size: Int | ||
get() = value.size | ||
|
||
val numbers: List<Int> | ||
get() = value.map { it.value } | ||
Comment on lines
+21
to
+22
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. 저는 값 객체로 감싼 값들은 어플리케이션의 종단에서만 꺼내서 사용하려고 하는 편입니다. 예를 들면 View에 값을 넣어주기 직전이나 DB 등에 값을 넣기 직전이 되겠죠. 원시값을 객체로 감쌀 때는 그 이유가 있을 거에요. 타입 구분을 위함이라던지 값에 로직을 넣는다던지요. 그런데 어플리케이션 중간에 그 값을 꺼내서 사용해버리면 이렇게 감싼 이유가 사라지게됩니다. LottoNumbers의 클라이언트 입장에서는 이 값이 로또 번호라는 보장을 잃게 되겠죠. |
||
|
||
fun contains(lottoNumber: LottoNumber): Boolean { | ||
return value.contains(lottoNumber) | ||
} | ||
|
||
fun containsCount(other: LottoNumbers): Int { | ||
return value.count { other.value.contains(it) } | ||
} | ||
|
||
private fun hasDuplicate(): Boolean { | ||
return value.distinct() != value | ||
} | ||
|
||
private fun isSorted(): Boolean { | ||
return value.map { it.value } | ||
.zipWithNext { a, b -> a <= b } | ||
Comment on lines
+37
to
+38
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. LottoNumber가 Comparable을 구현하면 값을 꺼내지 않고도 구현할 수 있을 것 같습니다. |
||
.all { it } | ||
} | ||
|
||
companion object { | ||
fun of(numbers: List<Int>): LottoNumbers { | ||
val lottoNumbers = numbers.map { LottoNumber(it) } | ||
|
||
return LottoNumbers(lottoNumbers) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package lotto.domain | ||
|
||
fun interface LottoNumbersGenerateStrategy { | ||
fun generate(): LottoNumbers | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,22 @@ | ||
package lotto.domain | ||
|
||
private const val LOTTO_PRICE = 1000 | ||
class LottoStore { | ||
private val lottoGenerator = LottoGenerator() | ||
|
||
class LottoStore(private val lottoGenerator: LottoGenerator) { | ||
fun buyLottos(money: Int): Lottos { | ||
fun buyLotto(lottoNumbersGenerateStrategy: LottoNumbersGenerateStrategy): Lotto { | ||
return lottoGenerator.publish(lottoNumbersGenerateStrategy) | ||
} | ||
|
||
fun buyLottos(money: Int, lottoNumbersGenerateStrategy: LottoNumbersGenerateStrategy): Lottos { | ||
Comment on lines
+6
to
+10
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. buyLotto는 수동 로또만, butLottos는 자동 로또만 만들고 있습니다. |
||
val lottoCount = money / LOTTO_PRICE | ||
val lottos = List(lottoCount) { | ||
lottoGenerator.publish() | ||
lottoGenerator.publish(lottoNumbersGenerateStrategy) | ||
} | ||
|
||
return Lottos(lottos) | ||
} | ||
|
||
companion object { | ||
const val LOTTO_PRICE = 1000 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package lotto.domain | ||
|
||
class ManualLottoNumbersGenerateStrategy( | ||
private val lottoNumbers: List<Int>, | ||
) : LottoNumbersGenerateStrategy { | ||
override fun generate(): LottoNumbers { | ||
return LottoNumbers.of(lottoNumbers) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package lotto.domain | ||
|
||
class MatchedLotto( | ||
val lotto: Lotto, | ||
val winning: LottoWinning, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package lotto.domain | ||
|
||
@JvmInline | ||
value class MatchedLottos(val value: List<MatchedLotto>) { | ||
fun totalReward(): Int { | ||
return value.sumOf { it.winning.reward } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,17 +2,18 @@ package lotto.ui | |
|
||
import lotto.domain.LottoWinning | ||
import lotto.domain.Lottos | ||
import lotto.domain.MatchedLottos | ||
import java.text.DecimalFormat | ||
|
||
object ResultView { | ||
fun showBoughtLottos(lottos: Lottos) { | ||
println("${lottos.value.size}개를 구매했습니다.") | ||
fun showBoughtLottos(lottos: Lottos, manualLottoCount: Int) { | ||
println("수동으로 ${manualLottoCount}장 자동으로 ${lottos.value.size - manualLottoCount}개를 구매했습니다.") | ||
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. 굳이 따져보자면 이런 빼기도 로직으로 볼 수 있을 것 같네요. |
||
lottos.value.forEach { | ||
println("numbers = ${it.numbers}") | ||
} | ||
} | ||
|
||
fun showResult(lottos: Lottos, returnRate: Double) { | ||
fun showResult(lottos: MatchedLottos, returnRate: Double) { | ||
val winningLottos = lottos.value | ||
.groupBy { it.winning } | ||
|
||
|
This file was deleted.
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.
돈에 관한 로직들이 생겨나고 있는 것 같은데요 돈에 관련된 값 객체 등을 만들어서 도메인에 로직을 두면 좋을 것 같아요.
지금은 컨트롤러에 로직들이 있어서 테스트가 어려워지는 것 같습니다.