From 3bac9d0682c03f2ea7f3f6190de181a4aa36d493 Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Mon, 25 Nov 2024 23:03:20 +0900 Subject: [PATCH 1/9] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Main.kt | 4 ++++ src/main/kotlin/lotto/{ => domain}/Lotto.kt | 2 +- src/main/kotlin/lotto/{ => domain}/LottoNumber.kt | 2 +- src/main/kotlin/lotto/{ => domain}/Order.kt | 4 +++- src/main/kotlin/lotto/{ => domain}/Prize.kt | 2 +- src/main/kotlin/lotto/{ => domain}/RankResult.kt | 2 +- src/main/kotlin/lotto/{ => domain}/WinningResult.kt | 2 +- src/main/kotlin/lotto/{ => service}/LottoSystem.kt | 8 +++++++- src/main/kotlin/lotto/{ => util}/NumberGenerator.kt | 2 +- src/main/kotlin/lotto/{ => util}/NumberSplitter.kt | 2 +- src/main/kotlin/lotto/{ => util}/RandomNumberGenerator.kt | 2 +- src/main/kotlin/lotto/{ => view}/InputView.kt | 4 +++- src/main/kotlin/lotto/{ => view}/ResultView.kt | 5 ++++- src/test/kotlin/lotto/FixedNumberGenerator.kt | 2 ++ src/test/kotlin/lotto/LottoNumberTest.kt | 1 + src/test/kotlin/lotto/LottoSystemTest.kt | 2 ++ src/test/kotlin/lotto/LottoTest.kt | 1 + src/test/kotlin/lotto/OrderTest.kt | 1 + src/test/kotlin/lotto/WinningResultTest.kt | 3 +++ 19 files changed, 39 insertions(+), 12 deletions(-) rename src/main/kotlin/lotto/{ => domain}/Lotto.kt (97%) rename src/main/kotlin/lotto/{ => domain}/LottoNumber.kt (94%) rename src/main/kotlin/lotto/{ => domain}/Order.kt (93%) rename src/main/kotlin/lotto/{ => domain}/Prize.kt (95%) rename src/main/kotlin/lotto/{ => domain}/RankResult.kt (89%) rename src/main/kotlin/lotto/{ => domain}/WinningResult.kt (98%) rename src/main/kotlin/lotto/{ => service}/LottoSystem.kt (71%) rename src/main/kotlin/lotto/{ => util}/NumberGenerator.kt (75%) rename src/main/kotlin/lotto/{ => util}/NumberSplitter.kt (93%) rename src/main/kotlin/lotto/{ => util}/RandomNumberGenerator.kt (90%) rename src/main/kotlin/lotto/{ => view}/InputView.kt (90%) rename src/main/kotlin/lotto/{ => view}/ResultView.kt (88%) diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index 1ddb9c0b0e..0cffbdf75a 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -1,5 +1,9 @@ package lotto +import lotto.service.LottoSystem +import lotto.view.InputView +import lotto.view.ResultView + fun main() { val lottoSystem = LottoSystem() diff --git a/src/main/kotlin/lotto/Lotto.kt b/src/main/kotlin/lotto/domain/Lotto.kt similarity index 97% rename from src/main/kotlin/lotto/Lotto.kt rename to src/main/kotlin/lotto/domain/Lotto.kt index 4c66c3fd35..944d14cb41 100644 --- a/src/main/kotlin/lotto/Lotto.kt +++ b/src/main/kotlin/lotto/domain/Lotto.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.domain class Lotto(generatedNumbers: Set) { val numbers: Set diff --git a/src/main/kotlin/lotto/LottoNumber.kt b/src/main/kotlin/lotto/domain/LottoNumber.kt similarity index 94% rename from src/main/kotlin/lotto/LottoNumber.kt rename to src/main/kotlin/lotto/domain/LottoNumber.kt index 9a54c08177..497139bb43 100644 --- a/src/main/kotlin/lotto/LottoNumber.kt +++ b/src/main/kotlin/lotto/domain/LottoNumber.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.domain @JvmInline value class LottoNumber(private val number: Int) { diff --git a/src/main/kotlin/lotto/Order.kt b/src/main/kotlin/lotto/domain/Order.kt similarity index 93% rename from src/main/kotlin/lotto/Order.kt rename to src/main/kotlin/lotto/domain/Order.kt index b6752337dd..4dc1291c3c 100644 --- a/src/main/kotlin/lotto/Order.kt +++ b/src/main/kotlin/lotto/domain/Order.kt @@ -1,4 +1,6 @@ -package lotto +package lotto.domain + +import lotto.util.NumberGenerator class Order( val amount: Int, diff --git a/src/main/kotlin/lotto/Prize.kt b/src/main/kotlin/lotto/domain/Prize.kt similarity index 95% rename from src/main/kotlin/lotto/Prize.kt rename to src/main/kotlin/lotto/domain/Prize.kt index 9fd7ba5646..4c670337de 100644 --- a/src/main/kotlin/lotto/Prize.kt +++ b/src/main/kotlin/lotto/domain/Prize.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.domain enum class Prize( val matchCount: Int, diff --git a/src/main/kotlin/lotto/RankResult.kt b/src/main/kotlin/lotto/domain/RankResult.kt similarity index 89% rename from src/main/kotlin/lotto/RankResult.kt rename to src/main/kotlin/lotto/domain/RankResult.kt index 0491a74883..aa15619ea0 100644 --- a/src/main/kotlin/lotto/RankResult.kt +++ b/src/main/kotlin/lotto/domain/RankResult.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.domain data class RankResult( val totalCount: Int, diff --git a/src/main/kotlin/lotto/WinningResult.kt b/src/main/kotlin/lotto/domain/WinningResult.kt similarity index 98% rename from src/main/kotlin/lotto/WinningResult.kt rename to src/main/kotlin/lotto/domain/WinningResult.kt index 2d9a2d2b4b..69377768d9 100644 --- a/src/main/kotlin/lotto/WinningResult.kt +++ b/src/main/kotlin/lotto/domain/WinningResult.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.domain class WinningResult( order: Order, diff --git a/src/main/kotlin/lotto/LottoSystem.kt b/src/main/kotlin/lotto/service/LottoSystem.kt similarity index 71% rename from src/main/kotlin/lotto/LottoSystem.kt rename to src/main/kotlin/lotto/service/LottoSystem.kt index 6f285d18f1..00bb1db44e 100644 --- a/src/main/kotlin/lotto/LottoSystem.kt +++ b/src/main/kotlin/lotto/service/LottoSystem.kt @@ -1,4 +1,10 @@ -package lotto +package lotto.service + +import lotto.domain.Lotto +import lotto.domain.Order +import lotto.domain.WinningResult +import lotto.util.NumberGenerator +import lotto.util.RandomNumberGenerator class LottoSystem(private val numberGenerator: NumberGenerator = RandomNumberGenerator()) { fun createOrder(amount: Int): Order { diff --git a/src/main/kotlin/lotto/NumberGenerator.kt b/src/main/kotlin/lotto/util/NumberGenerator.kt similarity index 75% rename from src/main/kotlin/lotto/NumberGenerator.kt rename to src/main/kotlin/lotto/util/NumberGenerator.kt index bea2f12dc7..30f87592b6 100644 --- a/src/main/kotlin/lotto/NumberGenerator.kt +++ b/src/main/kotlin/lotto/util/NumberGenerator.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.util interface NumberGenerator { fun generate(): Set diff --git a/src/main/kotlin/lotto/NumberSplitter.kt b/src/main/kotlin/lotto/util/NumberSplitter.kt similarity index 93% rename from src/main/kotlin/lotto/NumberSplitter.kt rename to src/main/kotlin/lotto/util/NumberSplitter.kt index 57b3600a81..0d6c369037 100644 --- a/src/main/kotlin/lotto/NumberSplitter.kt +++ b/src/main/kotlin/lotto/util/NumberSplitter.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.util object NumberSplitter { private const val SEPARATOR = "," diff --git a/src/main/kotlin/lotto/RandomNumberGenerator.kt b/src/main/kotlin/lotto/util/RandomNumberGenerator.kt similarity index 90% rename from src/main/kotlin/lotto/RandomNumberGenerator.kt rename to src/main/kotlin/lotto/util/RandomNumberGenerator.kt index 641a06f135..e8503bf799 100644 --- a/src/main/kotlin/lotto/RandomNumberGenerator.kt +++ b/src/main/kotlin/lotto/util/RandomNumberGenerator.kt @@ -1,4 +1,4 @@ -package lotto +package lotto.util class RandomNumberGenerator : NumberGenerator { override fun generate(): Set { diff --git a/src/main/kotlin/lotto/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt similarity index 90% rename from src/main/kotlin/lotto/InputView.kt rename to src/main/kotlin/lotto/view/InputView.kt index a2c8707407..c0e10feda4 100644 --- a/src/main/kotlin/lotto/InputView.kt +++ b/src/main/kotlin/lotto/view/InputView.kt @@ -1,4 +1,6 @@ -package lotto +package lotto.view + +import lotto.util.NumberSplitter object InputView { fun getAmount(): Int { diff --git a/src/main/kotlin/lotto/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt similarity index 88% rename from src/main/kotlin/lotto/ResultView.kt rename to src/main/kotlin/lotto/view/ResultView.kt index dd2ec1a79c..369b2eb87b 100644 --- a/src/main/kotlin/lotto/ResultView.kt +++ b/src/main/kotlin/lotto/view/ResultView.kt @@ -1,4 +1,7 @@ -package lotto +package lotto.view + +import lotto.domain.Lotto +import lotto.domain.WinningResult object ResultView { fun printCreatedLottos(lottos: List) { diff --git a/src/test/kotlin/lotto/FixedNumberGenerator.kt b/src/test/kotlin/lotto/FixedNumberGenerator.kt index b8de67db43..6ef14a99e9 100644 --- a/src/test/kotlin/lotto/FixedNumberGenerator.kt +++ b/src/test/kotlin/lotto/FixedNumberGenerator.kt @@ -1,5 +1,7 @@ package lotto +import lotto.util.NumberGenerator + class FixedNumberGenerator : NumberGenerator { override fun generate(): Set { return setOf(1, 2, 3, 4, 5, 6) diff --git a/src/test/kotlin/lotto/LottoNumberTest.kt b/src/test/kotlin/lotto/LottoNumberTest.kt index 4fdaa75464..078f48c468 100644 --- a/src/test/kotlin/lotto/LottoNumberTest.kt +++ b/src/test/kotlin/lotto/LottoNumberTest.kt @@ -3,6 +3,7 @@ package lotto import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec +import lotto.domain.LottoNumber class LottoNumberTest : StringSpec({ "로또 반허 생성시 1 ~ 45를 벗어난 숫자가 전달될 경우 예외를 반환한다." { diff --git a/src/test/kotlin/lotto/LottoSystemTest.kt b/src/test/kotlin/lotto/LottoSystemTest.kt index d8fb797b33..f55241b10d 100644 --- a/src/test/kotlin/lotto/LottoSystemTest.kt +++ b/src/test/kotlin/lotto/LottoSystemTest.kt @@ -3,6 +3,8 @@ package lotto import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import lotto.domain.Lotto +import lotto.service.LottoSystem class LottoSystemTest : StringSpec({ "로또 금액을 입력받으면 해당 요청에 대한 주문을 생성해야한다." { diff --git a/src/test/kotlin/lotto/LottoTest.kt b/src/test/kotlin/lotto/LottoTest.kt index 1eb2c61cb0..c4f8c5672d 100644 --- a/src/test/kotlin/lotto/LottoTest.kt +++ b/src/test/kotlin/lotto/LottoTest.kt @@ -4,6 +4,7 @@ import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import lotto.domain.Lotto class LottoTest : StringSpec({ "한장의 로또는 6개의 숫자로 구성된다." { diff --git a/src/test/kotlin/lotto/OrderTest.kt b/src/test/kotlin/lotto/OrderTest.kt index 9c415851a7..1201042cd1 100644 --- a/src/test/kotlin/lotto/OrderTest.kt +++ b/src/test/kotlin/lotto/OrderTest.kt @@ -4,6 +4,7 @@ import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import lotto.domain.Order class OrderTest : StringSpec({ "주문이 생성되면 전달된 금액에 맞는 로또를 소지한다." { diff --git a/src/test/kotlin/lotto/WinningResultTest.kt b/src/test/kotlin/lotto/WinningResultTest.kt index 4986d734b5..a5d4818017 100644 --- a/src/test/kotlin/lotto/WinningResultTest.kt +++ b/src/test/kotlin/lotto/WinningResultTest.kt @@ -3,6 +3,9 @@ package lotto import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import lotto.domain.Lotto +import lotto.domain.Order +import lotto.domain.WinningResult class WinningResultTest : StringSpec({ "3개, 4개, 5개, 6개 에 대해 각각 몇개씩 일치하는지 정보를 제공한다." { From 7699cd2c929f48d5ca684fa28f8a485b7a259357 Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Tue, 26 Nov 2024 01:08:11 +0900 Subject: [PATCH 2/9] =?UTF-8?q?refactor:=202=EB=8B=A8=EA=B3=84=20=ED=94=BC?= =?UTF-8?q?=EB=93=9C=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Main.kt | 12 ++-- src/main/kotlin/lotto/const/LottoConst.kt | 8 +++ src/main/kotlin/lotto/domain/Lotto.kt | 13 +--- src/main/kotlin/lotto/domain/Order.kt | 26 +++----- src/main/kotlin/lotto/domain/Prize.kt | 2 + src/main/kotlin/lotto/domain/WinningLotto.kt | 7 +++ src/main/kotlin/lotto/domain/WinningResult.kt | 49 --------------- src/main/kotlin/lotto/service/LottoCreator.kt | 25 ++++++++ src/main/kotlin/lotto/service/LottoSystem.kt | 24 -------- src/main/kotlin/lotto/service/OrderService.kt | 23 ++++++++ .../lotto/service/WinningLottoService.kt | 59 +++++++++++++++++++ src/main/kotlin/lotto/view/ResultView.kt | 2 +- .../kotlin/lotto/view/dto/WinningResult.kt | 9 +++ src/test/kotlin/lotto/LottoCreatorTest.kt | 25 ++++++++ src/test/kotlin/lotto/LottoSystemTest.kt | 41 ------------- src/test/kotlin/lotto/LottoTest.kt | 34 +++++------ src/test/kotlin/lotto/OrderServiceTest.kt | 39 ++++++++++++ src/test/kotlin/lotto/OrderTest.kt | 43 +++++++++++--- .../kotlin/lotto/WinningLottoServiceTest.kt | 58 ++++++++++++++++++ src/test/kotlin/lotto/WinningResultTest.kt | 42 ------------- 20 files changed, 324 insertions(+), 217 deletions(-) create mode 100644 src/main/kotlin/lotto/const/LottoConst.kt create mode 100644 src/main/kotlin/lotto/domain/WinningLotto.kt delete mode 100644 src/main/kotlin/lotto/domain/WinningResult.kt create mode 100644 src/main/kotlin/lotto/service/LottoCreator.kt delete mode 100644 src/main/kotlin/lotto/service/LottoSystem.kt create mode 100644 src/main/kotlin/lotto/service/OrderService.kt create mode 100644 src/main/kotlin/lotto/service/WinningLottoService.kt create mode 100644 src/main/kotlin/lotto/view/dto/WinningResult.kt create mode 100644 src/test/kotlin/lotto/LottoCreatorTest.kt delete mode 100644 src/test/kotlin/lotto/LottoSystemTest.kt create mode 100644 src/test/kotlin/lotto/OrderServiceTest.kt create mode 100644 src/test/kotlin/lotto/WinningLottoServiceTest.kt delete mode 100644 src/test/kotlin/lotto/WinningResultTest.kt diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index 0cffbdf75a..c8ca5c3846 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -1,19 +1,21 @@ package lotto -import lotto.service.LottoSystem +import lotto.service.OrderService +import lotto.service.WinningLottoService import lotto.view.InputView import lotto.view.ResultView fun main() { - val lottoSystem = LottoSystem() + val orderService = OrderService() + val winningLottoService = WinningLottoService() val amount = InputView.getAmount() - val order = lottoSystem.createOrder(amount) + val order = orderService.makeOrder(amount) ResultView.printCreatedLottos(order.lottos) val winNumberInput = InputView.getWinNumberInput() - val winNumbers = lottoSystem.createWinNumbers(winNumberInput) + val winNumbers = winningLottoService.createWinningLotto(winNumberInput) - val result = lottoSystem.createWinningResult(order, winNumbers) + val result = winningLottoService.checkAndGetResult(order, winNumbers) ResultView.printResult(result) } diff --git a/src/main/kotlin/lotto/const/LottoConst.kt b/src/main/kotlin/lotto/const/LottoConst.kt new file mode 100644 index 0000000000..417884f1c0 --- /dev/null +++ b/src/main/kotlin/lotto/const/LottoConst.kt @@ -0,0 +1,8 @@ +package lotto.const + +import lotto.domain.LottoNumber + +object LottoConst { + const val UNIT_OF_AMOUNT = 1000 + val LOTTO_NUMBERS = IntRange(1, 45).map { LottoNumber(it) }.toList() +} diff --git a/src/main/kotlin/lotto/domain/Lotto.kt b/src/main/kotlin/lotto/domain/Lotto.kt index 944d14cb41..f3a8991819 100644 --- a/src/main/kotlin/lotto/domain/Lotto.kt +++ b/src/main/kotlin/lotto/domain/Lotto.kt @@ -1,18 +1,11 @@ package lotto.domain -class Lotto(generatedNumbers: Set) { - val numbers: Set - +class Lotto(val numbers: Set) { init { - validateSize(generatedNumbers) - numbers = generatedNumbers.map { LottoNumber(it) }.toSet() - } - - fun countMatchingNumbers(targetLotto: Set): Int { - return targetLotto.count { it in this.numbers } + validateSize(numbers) } - private fun validateSize(numbers: Set) { + private fun validateSize(numbers: Set) { require(numbers.size == LOTTO_NUMBER_SIZE) { "로또 번호는 ${LOTTO_NUMBER_SIZE}개여야 합니다. 현재 전달된 개수는 ${numbers.size}개 입니다." } } diff --git a/src/main/kotlin/lotto/domain/Order.kt b/src/main/kotlin/lotto/domain/Order.kt index 4dc1291c3c..9dab741b8a 100644 --- a/src/main/kotlin/lotto/domain/Order.kt +++ b/src/main/kotlin/lotto/domain/Order.kt @@ -1,29 +1,17 @@ package lotto.domain -import lotto.util.NumberGenerator +import lotto.const.LottoConst.UNIT_OF_AMOUNT -class Order( +data class Order( val amount: Int, - numberGenerator: NumberGenerator, + val lottos: List, ) { - val lottos: List - init { - validatePositive() - val lottoCount = calculateLottoCounts() - lottos = List(lottoCount) { Lotto(numberGenerator.generate()) } - } - - private fun validatePositive() { - require(amount > 0) { "로또 구매 금액은 음수이거나 0원일 수 없습니다. (현재 입력 금액: $amount)" } - } - - private fun calculateLottoCounts(): Int { - require(amount % UNIT_OF_AMOUNT == 0) { "로또 구매 금액은 1000원 단위로 입력되어야 합니다. (현재 입력 금액: $amount)" } - return amount / UNIT_OF_AMOUNT + validateLottoCounts() } - companion object { - private const val UNIT_OF_AMOUNT = 1000 + private fun validateLottoCounts() { + val count = amount / UNIT_OF_AMOUNT + require(lottos.size == count) { "구매한 금액과 로또의 수량이 일치하지 않습니다." } } } diff --git a/src/main/kotlin/lotto/domain/Prize.kt b/src/main/kotlin/lotto/domain/Prize.kt index 4c670337de..e26a53bb8c 100644 --- a/src/main/kotlin/lotto/domain/Prize.kt +++ b/src/main/kotlin/lotto/domain/Prize.kt @@ -10,6 +10,8 @@ enum class Prize( FOURTH(3, 5_000), ; companion object { + val RANK_RANGE = IntRange(3, 6) + fun findByMatchCount(matchCount: Int): Prize { return entries.find { it.matchCount == matchCount } ?: throw RuntimeException("일치하는 숫자의 개수에 해당하는 상품이 존재하지 않습니다.") diff --git a/src/main/kotlin/lotto/domain/WinningLotto.kt b/src/main/kotlin/lotto/domain/WinningLotto.kt new file mode 100644 index 0000000000..344bede3a1 --- /dev/null +++ b/src/main/kotlin/lotto/domain/WinningLotto.kt @@ -0,0 +1,7 @@ +package lotto.domain + +class WinningLotto(val winningNumbers: Lotto) { + fun countMatchingNumbers(targetLotto: Lotto): Int { + return targetLotto.numbers.count { it in winningNumbers.numbers } + } +} diff --git a/src/main/kotlin/lotto/domain/WinningResult.kt b/src/main/kotlin/lotto/domain/WinningResult.kt deleted file mode 100644 index 69377768d9..0000000000 --- a/src/main/kotlin/lotto/domain/WinningResult.kt +++ /dev/null @@ -1,49 +0,0 @@ -package lotto.domain - -class WinningResult( - order: Order, - winNumbers: Lotto, -) { - val winningMatchCounts: List - val revenue: Int - val rate: Double - - init { - winningMatchCounts = createRankResults(order, winNumbers) - revenue = calculateRevenue() - rate = revenue.toDouble() / order.amount.toDouble() - } - - private fun calculateRevenue(): Int { - return winningMatchCounts.sumOf { it.getTotalPrizeMoney() } - } - - // 3 ~ 6 순서로 정렬된 List를 반환한다. - private fun createRankResults( - order: Order, - winNumbers: Lotto, - ): List { - val result = groupByRanks(order, winNumbers) - return RANK_RANGE.map { rank -> - RankResult( - totalCount = result[rank] ?: 0, - prize = Prize.findByMatchCount(rank), - ) - } - .sortedBy { it.prize.matchCount } - } - - private fun groupByRanks( - order: Order, - winNumbers: Lotto, - ): Map { - return order.lottos.map { winNumbers.countMatchingNumbers(it.numbers) } - .filter { it >= RANK_RANGE.first } - .groupBy { it } - .mapValues { (_, values) -> values.size } - } - - companion object { - private val RANK_RANGE = IntRange(3, 6) - } -} diff --git a/src/main/kotlin/lotto/service/LottoCreator.kt b/src/main/kotlin/lotto/service/LottoCreator.kt new file mode 100644 index 0000000000..657e38b527 --- /dev/null +++ b/src/main/kotlin/lotto/service/LottoCreator.kt @@ -0,0 +1,25 @@ +package lotto.service + +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 { + return List(count) { createSingleLotto() } + } + + fun createWinningLotto(winningNumbers: Set): WinningLotto { + val winningLotto = Lotto(winningNumbers.map { LottoNumber(it) }.toSet()) + return WinningLotto(winningLotto) + } + + private fun createSingleLotto(): Lotto { + val randomNumbers = numberGenerator.generate() + val lotto = Lotto(randomNumbers.map { LottoConst.LOTTO_NUMBERS[it - 1] }.toSet()) + return lotto + } +} diff --git a/src/main/kotlin/lotto/service/LottoSystem.kt b/src/main/kotlin/lotto/service/LottoSystem.kt deleted file mode 100644 index 00bb1db44e..0000000000 --- a/src/main/kotlin/lotto/service/LottoSystem.kt +++ /dev/null @@ -1,24 +0,0 @@ -package lotto.service - -import lotto.domain.Lotto -import lotto.domain.Order -import lotto.domain.WinningResult -import lotto.util.NumberGenerator -import lotto.util.RandomNumberGenerator - -class LottoSystem(private val numberGenerator: NumberGenerator = RandomNumberGenerator()) { - fun createOrder(amount: Int): Order { - return Order(amount, numberGenerator) - } - - fun createWinNumbers(winningNumbers: Set): Lotto { - return Lotto(winningNumbers) - } - - fun createWinningResult( - order: Order, - winNumbers: Lotto, - ): WinningResult { - return WinningResult(order, winNumbers) - } -} diff --git a/src/main/kotlin/lotto/service/OrderService.kt b/src/main/kotlin/lotto/service/OrderService.kt new file mode 100644 index 0000000000..6560dd9053 --- /dev/null +++ b/src/main/kotlin/lotto/service/OrderService.kt @@ -0,0 +1,23 @@ +package lotto.service + +import lotto.const.LottoConst.UNIT_OF_AMOUNT +import lotto.domain.Order + +class OrderService() { + 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 + } +} diff --git a/src/main/kotlin/lotto/service/WinningLottoService.kt b/src/main/kotlin/lotto/service/WinningLottoService.kt new file mode 100644 index 0000000000..2ae5e128c6 --- /dev/null +++ b/src/main/kotlin/lotto/service/WinningLottoService.kt @@ -0,0 +1,59 @@ +package lotto.service + +import lotto.domain.Order +import lotto.domain.Prize +import lotto.domain.RankResult +import lotto.domain.WinningLotto +import lotto.view.dto.WinningResult + +class WinningLottoService { + private val lottoCreator = LottoCreator() + + fun createWinningLotto(winningNumbers: Set): WinningLotto { + return lottoCreator.createWinningLotto(winningNumbers) + } + + fun checkAndGetResult( + order: Order, + winningLotto: WinningLotto, + ): WinningResult { + val winningMatchCounts = createRankResults(order, winningLotto) + val revenue = calculateRevenue(winningMatchCounts) + val rate = revenue.toDouble() / order.amount.toDouble() + + return WinningResult(winningMatchCounts, revenue, rate) + } + + private fun calculateRevenue(matchCounts: List): Int { + return matchCounts.sumOf { it.getTotalPrizeMoney() } + } + + // 3 ~ 6 순서로 정렬된 List를 반환한다. + private fun createRankResults( + order: Order, + winNumbers: WinningLotto, + ): List { + val result = groupByRanks(order, winNumbers) + return convertToRankResult(result) + } + + private fun groupByRanks( + order: Order, + winNumbers: WinningLotto, + ): Map { + return order.lottos.map { winNumbers.countMatchingNumbers(it) } + .filter { it >= Prize.RANK_RANGE.first } + .groupBy { it } + .mapValues { (_, values) -> values.size } + } + + private fun convertToRankResult(result: Map): List { + return Prize.RANK_RANGE.map { rank -> + RankResult( + totalCount = result[rank] ?: 0, + prize = Prize.findByMatchCount(rank), + ) + } + .sortedBy { it.prize.matchCount } + } +} diff --git a/src/main/kotlin/lotto/view/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt index 369b2eb87b..6529bdd5aa 100644 --- a/src/main/kotlin/lotto/view/ResultView.kt +++ b/src/main/kotlin/lotto/view/ResultView.kt @@ -1,7 +1,7 @@ package lotto.view import lotto.domain.Lotto -import lotto.domain.WinningResult +import lotto.view.dto.WinningResult object ResultView { fun printCreatedLottos(lottos: List) { diff --git a/src/main/kotlin/lotto/view/dto/WinningResult.kt b/src/main/kotlin/lotto/view/dto/WinningResult.kt new file mode 100644 index 0000000000..c74ebbfe1a --- /dev/null +++ b/src/main/kotlin/lotto/view/dto/WinningResult.kt @@ -0,0 +1,9 @@ +package lotto.view.dto + +import lotto.domain.RankResult + +data class WinningResult( + val winningMatchCounts: List, + val revenue: Int, + val rate: Double, +) diff --git a/src/test/kotlin/lotto/LottoCreatorTest.kt b/src/test/kotlin/lotto/LottoCreatorTest.kt new file mode 100644 index 0000000000..8a13c171da --- /dev/null +++ b/src/test/kotlin/lotto/LottoCreatorTest.kt @@ -0,0 +1,25 @@ +package lotto + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import lotto.domain.Lotto +import lotto.domain.LottoNumber +import lotto.service.LottoCreator + +class LottoCreatorTest : StringSpec({ + "입력된 개수만큼의 로또를 생성할 수 있다." { + val lottoCreator = LottoCreator(FixedNumberGenerator()) + + val result = lottoCreator.createLottos(5) + + result.size shouldBe 5 + } + + "입력된 숫자를 가진 당첨 로또를 생성할 수 있다." { + val lottoCreator = LottoCreator(FixedNumberGenerator()) + + val result = lottoCreator.createWinningLotto(FixedNumberGenerator().generate()) + + result.winningNumbers shouldBe Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()) + } +}) diff --git a/src/test/kotlin/lotto/LottoSystemTest.kt b/src/test/kotlin/lotto/LottoSystemTest.kt deleted file mode 100644 index f55241b10d..0000000000 --- a/src/test/kotlin/lotto/LottoSystemTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -package lotto - -import io.kotest.assertions.assertSoftly -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe -import lotto.domain.Lotto -import lotto.service.LottoSystem - -class LottoSystemTest : StringSpec({ - "로또 금액을 입력받으면 해당 요청에 대한 주문을 생성해야한다." { - val lottoSystem = LottoSystem(FixedNumberGenerator()) - val order = lottoSystem.createOrder(10000) - - assertSoftly { - order.amount shouldBe 10000 - order.lottos.size shouldBe 10 - } - } - - "당첨 번호를 입력받을 경우 당첨 번호 객체를 생성해야 한다." { - val lottoSystem = LottoSystem(FixedNumberGenerator()) - - val winNumbers = lottoSystem.createWinNumbers(setOf(1, 2, 3, 4, 5, 6)) - - winNumbers shouldBe Lotto(FixedNumberGenerator().generate()) - } - - "당첨 통계 결과를 제공해야 한다." { - val lottoSystem = LottoSystem(FixedNumberGenerator()) - val order = lottoSystem.createOrder(1000) - val winNumbers = lottoSystem.createWinNumbers(setOf(1, 2, 3, 8, 9, 10)) - - val result = lottoSystem.createWinningResult(order, winNumbers) - - assertSoftly { - result.revenue shouldBe 5_000 - result.winningMatchCounts[0].totalCount shouldBe 1 - result.rate shouldBe 5.0 - } - } -}) diff --git a/src/test/kotlin/lotto/LottoTest.kt b/src/test/kotlin/lotto/LottoTest.kt index c4f8c5672d..e6e9896128 100644 --- a/src/test/kotlin/lotto/LottoTest.kt +++ b/src/test/kotlin/lotto/LottoTest.kt @@ -5,10 +5,22 @@ import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import lotto.domain.Lotto +import lotto.domain.LottoNumber class LottoTest : StringSpec({ + val correctLottoNumbers = + setOf( + LottoNumber(1), + LottoNumber(2), + LottoNumber(3), + LottoNumber(4), + LottoNumber(5), + LottoNumber(6), + ) + "한장의 로또는 6개의 숫자로 구성된다." { - val lotto = Lotto(setOf(1, 2, 3, 4, 5, 6)) + + val lotto = Lotto(correctLottoNumbers) lotto.numbers.size shouldBe 6 } @@ -16,24 +28,8 @@ class LottoTest : StringSpec({ "한장의 로또는 6개 미만 혹은 6개 초과된 숫자가 전달될 경우 예외를 반환한다." { assertSoftly { shouldThrow { Lotto(setOf()) } - shouldThrow { Lotto(setOf(1, 2, 3, 4, 5)) } - shouldThrow { Lotto(setOf(1, 2, 3, 4, 5, 6, 7)) } - } - } - - "로또 생성시 1 ~ 45를 벗어난 숫자들이 전달될 경우 예외를 반환한다." { - assertSoftly { - shouldThrow { Lotto(setOf(1, 2, 3, 4, 5, 46)) } - shouldThrow { Lotto(setOf(0, 2, 3, 4, 5, 6)) } + shouldThrow { Lotto(setOf(LottoNumber(1))) } + shouldThrow { Lotto(correctLottoNumbers + LottoNumber(7)) } } } - - "당첨 번호는 전달받은 로또 중 몇개의 값이 일치하는지 계산할 수 있다." { - val winNumbers = Lotto(setOf(1, 2, 3, 4, 5, 6)) - val lotto = Lotto(setOf(1, 2, 7, 8, 9, 10)) - - val result = winNumbers.countMatchingNumbers(lotto.numbers) - - result shouldBe 2 - } }) diff --git a/src/test/kotlin/lotto/OrderServiceTest.kt b/src/test/kotlin/lotto/OrderServiceTest.kt new file mode 100644 index 0000000000..5860298859 --- /dev/null +++ b/src/test/kotlin/lotto/OrderServiceTest.kt @@ -0,0 +1,39 @@ +package lotto + +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import lotto.service.OrderService + +class OrderServiceTest : StringSpec({ + "로또 금액을 입력받으면 해당 요청에 대한 주문을 생성해야한다." { + val orderService = OrderService() + val order = orderService.makeOrder(10000) + + assertSoftly { + order.amount shouldBe 10000 + order.lottos.size shouldBe 10 + } + } + + "주문 생성 시 전달된 금액이 1000원 단위가 아닐 경우 예외를 반환한다." { + val orderService = OrderService() + shouldThrow { orderService.makeOrder(10001) } + } + + // + // "당첨 통계 결과를 제공해야 한다." { + // val lottoSystem = LottoSystem(FixedNumberGenerator()) + // val order = lottoSystem.createOrder(1000) + // val winNumbers = lottoSystem.createWinNumbers(setOf(1, 2, 3, 8, 9, 10)) + // + // val result = lottoSystem.createWinningResult(order, winNumbers) + // + // assertSoftly { + // result.revenue shouldBe 5_000 + // result.winningMatchCounts[0].totalCount shouldBe 1 + // result.rate shouldBe 5.0 + // } + // } +}) diff --git a/src/test/kotlin/lotto/OrderTest.kt b/src/test/kotlin/lotto/OrderTest.kt index 1201042cd1..60b53d8182 100644 --- a/src/test/kotlin/lotto/OrderTest.kt +++ b/src/test/kotlin/lotto/OrderTest.kt @@ -4,23 +4,52 @@ import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import lotto.domain.Lotto +import lotto.domain.LottoNumber import lotto.domain.Order class OrderTest : StringSpec({ "주문이 생성되면 전달된 금액에 맞는 로또를 소지한다." { - val order = Order(5000, FixedNumberGenerator()) + val lottoNumbers = + setOf( + LottoNumber(1), + LottoNumber(2), + LottoNumber(3), + LottoNumber(4), + LottoNumber(5), + LottoNumber(6), + ) + val lottos = + listOf( + Lotto(lottoNumbers), + Lotto(lottoNumbers), + Lotto(lottoNumbers), + Lotto(lottoNumbers), + Lotto(lottoNumbers), + ) + + val order = Order(5000, lottos) order.lottos.size shouldBe 5 } - "주문 생성 시 전달된 금액이 1000원 단위가 아닐 경우 예외를 반환한다." { - shouldThrow { Order(10001, FixedNumberGenerator()) } - } + "주문 생성 시 전달된 금액과 로또의 수량이 맞지 않을 경우 예외를 반환한다." { - "주문 생성 시 전달된 금액이 음수 혹은 0원일 경우 예외를 반환한다." { assertSoftly { - shouldThrow { Order(-1, FixedNumberGenerator()) } - shouldThrow { Order(0, FixedNumberGenerator()) } + shouldThrow { Order(10000, listOf(Lotto(setOf(LottoNumber(1))))) } + shouldThrow { + Order( + 10000, + listOf( + Lotto( + setOf( + LottoNumber(1), + LottoNumber(2), + ), + ), + ), + ) + } } } }) diff --git a/src/test/kotlin/lotto/WinningLottoServiceTest.kt b/src/test/kotlin/lotto/WinningLottoServiceTest.kt new file mode 100644 index 0000000000..dce3e106c9 --- /dev/null +++ b/src/test/kotlin/lotto/WinningLottoServiceTest.kt @@ -0,0 +1,58 @@ +package lotto + +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import lotto.domain.Lotto +import lotto.domain.LottoNumber +import lotto.domain.Order +import lotto.domain.WinningLotto +import lotto.service.WinningLottoService + +class WinningLottoServiceTest : StringSpec({ + "3개, 4개, 5개, 6개 에 대해 각각 몇개씩 일치하는지 정보를 제공한다." { + val lotto = + Lotto( + setOf( + LottoNumber(1), + LottoNumber(2), + LottoNumber(3), + LottoNumber(7), + LottoNumber(8), + LottoNumber(9), + ), + ) + val winningLottoService = WinningLottoService() + val order = Order(1000, listOf(lotto)) + val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet())) + + val result = winningLottoService.checkAndGetResult(order, winNumbers) + + assertSoftly { + result.winningMatchCounts[0].totalCount shouldBe 1 + result.winningMatchCounts[1].totalCount shouldBe 0 + result.winningMatchCounts[2].totalCount shouldBe 0 + result.winningMatchCounts[3].totalCount shouldBe 0 + } + } + + "수익을 제공한다." { + val winningLottoService = WinningLottoService() + val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) + val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet())) + + val result = winningLottoService.checkAndGetResult(order, winNumbers) + + result.revenue shouldBe 2_000_000_000 + } + + "수익률을 제공한다." { + val winningLottoService = WinningLottoService() + val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) + val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet())) + + val result = winningLottoService.checkAndGetResult(order, winNumbers) + + result.rate shouldBe 2000000.0 + } +}) diff --git a/src/test/kotlin/lotto/WinningResultTest.kt b/src/test/kotlin/lotto/WinningResultTest.kt deleted file mode 100644 index a5d4818017..0000000000 --- a/src/test/kotlin/lotto/WinningResultTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -package lotto - -import io.kotest.assertions.assertSoftly -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe -import lotto.domain.Lotto -import lotto.domain.Order -import lotto.domain.WinningResult - -class WinningResultTest : StringSpec({ - "3개, 4개, 5개, 6개 에 대해 각각 몇개씩 일치하는지 정보를 제공한다." { - val order = Order(1000, FixedNumberGenerator()) - val winNumbers = Lotto(FixedNumberGenerator().generate()) - - val result = WinningResult(order, winNumbers) - - assertSoftly { - result.winningMatchCounts[0].totalCount shouldBe 0 - result.winningMatchCounts[1].totalCount shouldBe 0 - result.winningMatchCounts[2].totalCount shouldBe 0 - result.winningMatchCounts[3].totalCount shouldBe 1 - } - } - - "수익을 제공한다." { - val order = Order(1000, FixedNumberGenerator()) - val winNumbers = Lotto(FixedNumberGenerator().generate()) - - val result = WinningResult(order, winNumbers) - - result.revenue shouldBe 2_000_000_000 - } - - "수익률을 제공한다." { - val order = Order(1000, FixedNumberGenerator()) - val winNumbers = Lotto(FixedNumberGenerator().generate()) - - val result = WinningResult(order, winNumbers) - - result.rate shouldBe 2000000.0 - } -}) From 64b4a78579b8c75aba377a3161d50c9de5b7b839 Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Tue, 26 Nov 2024 15:35:53 +0900 Subject: [PATCH 3/9] =?UTF-8?q?refactor:=20Prize=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EC=9D=98=20=EC=9D=B4=EB=A6=84=EC=9D=84=20Rank?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=ED=8C=90=EB=B3=84?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/domain/Prize.kt | 20 -------------------- src/main/kotlin/lotto/domain/Rank.kt | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 20 deletions(-) delete mode 100644 src/main/kotlin/lotto/domain/Prize.kt create mode 100644 src/main/kotlin/lotto/domain/Rank.kt diff --git a/src/main/kotlin/lotto/domain/Prize.kt b/src/main/kotlin/lotto/domain/Prize.kt deleted file mode 100644 index e26a53bb8c..0000000000 --- a/src/main/kotlin/lotto/domain/Prize.kt +++ /dev/null @@ -1,20 +0,0 @@ -package lotto.domain - -enum class Prize( - val matchCount: Int, - val prizeAmount: Int, -) { - FIRST(6, 2_000_000_000), - SECOND(5, 1_500_000), - THIRD(4, 50_000), - FOURTH(3, 5_000), ; - - companion object { - val RANK_RANGE = IntRange(3, 6) - - fun findByMatchCount(matchCount: Int): Prize { - return entries.find { it.matchCount == matchCount } - ?: throw RuntimeException("일치하는 숫자의 개수에 해당하는 상품이 존재하지 않습니다.") - } - } -} diff --git a/src/main/kotlin/lotto/domain/Rank.kt b/src/main/kotlin/lotto/domain/Rank.kt new file mode 100644 index 0000000000..a3969f0571 --- /dev/null +++ b/src/main/kotlin/lotto/domain/Rank.kt @@ -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("일치하는 숫자의 개수에 해당하는 상품이 존재하지 않습니다.") + } + } +} From 2e35465b0eee87295254dcb7dbc02b8707bebf63 Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Tue, 26 Nov 2024 15:36:36 +0900 Subject: [PATCH 4/9] =?UTF-8?q?refactor:=20=EB=8B=B9=EC=B2=A8=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/domain/RankResult.kt | 10 --- src/main/kotlin/lotto/domain/WinningLotto.kt | 9 ++- .../lotto/service/WinningLottoService.kt | 68 ++++++++++--------- .../kotlin/lotto/view/dto/WinningResult.kt | 6 +- .../kotlin/lotto/WinningLottoServiceTest.kt | 29 ++++++-- src/test/kotlin/lotto/WinningLottoTest.kt | 27 ++++++++ 6 files changed, 99 insertions(+), 50 deletions(-) delete mode 100644 src/main/kotlin/lotto/domain/RankResult.kt create mode 100644 src/test/kotlin/lotto/WinningLottoTest.kt diff --git a/src/main/kotlin/lotto/domain/RankResult.kt b/src/main/kotlin/lotto/domain/RankResult.kt deleted file mode 100644 index aa15619ea0..0000000000 --- a/src/main/kotlin/lotto/domain/RankResult.kt +++ /dev/null @@ -1,10 +0,0 @@ -package lotto.domain - -data class RankResult( - val totalCount: Int, - val prize: Prize, -) { - fun getTotalPrizeMoney(): Int { - return this.prize.prizeAmount * totalCount - } -} diff --git a/src/main/kotlin/lotto/domain/WinningLotto.kt b/src/main/kotlin/lotto/domain/WinningLotto.kt index 344bede3a1..b31e99f51c 100644 --- a/src/main/kotlin/lotto/domain/WinningLotto.kt +++ b/src/main/kotlin/lotto/domain/WinningLotto.kt @@ -1,7 +1,14 @@ package lotto.domain -class WinningLotto(val winningNumbers: Lotto) { +class WinningLotto( + val winningNumbers: Lotto, + val bonusNumber: LottoNumber, +) { fun countMatchingNumbers(targetLotto: Lotto): Int { return targetLotto.numbers.count { it in winningNumbers.numbers } } + + fun matchBonusNumber(targetLotto: Lotto): Boolean { + return targetLotto.numbers.contains(bonusNumber) + } } diff --git a/src/main/kotlin/lotto/service/WinningLottoService.kt b/src/main/kotlin/lotto/service/WinningLottoService.kt index 2ae5e128c6..3475b1ed87 100644 --- a/src/main/kotlin/lotto/service/WinningLottoService.kt +++ b/src/main/kotlin/lotto/service/WinningLottoService.kt @@ -1,59 +1,63 @@ package lotto.service +import lotto.domain.Lotto +import lotto.domain.LottoResult import lotto.domain.Order -import lotto.domain.Prize -import lotto.domain.RankResult +import lotto.domain.Rank import lotto.domain.WinningLotto import lotto.view.dto.WinningResult +import kotlin.math.roundToInt class WinningLottoService { private val lottoCreator = LottoCreator() - fun createWinningLotto(winningNumbers: Set): WinningLotto { - return lottoCreator.createWinningLotto(winningNumbers) + fun createWinningLotto( + winningNumbers: Set, + bonusNumber: Int, + ): WinningLotto { + return lottoCreator.createWinningLotto(winningNumbers, bonusNumber) } fun checkAndGetResult( order: Order, winningLotto: WinningLotto, ): WinningResult { - val winningMatchCounts = createRankResults(order, winningLotto) + val winningMatchCounts = aggregateLottoResult(order.lottos, winningLotto) val revenue = calculateRevenue(winningMatchCounts) - val rate = revenue.toDouble() / order.amount.toDouble() + val rate = (revenue.toDouble() / order.amount.toDouble()).roundToInt() return WinningResult(winningMatchCounts, revenue, rate) } - private fun calculateRevenue(matchCounts: List): Int { + private fun calculateRevenue(matchCounts: List): Int { return matchCounts.sumOf { it.getTotalPrizeMoney() } } - // 3 ~ 6 순서로 정렬된 List를 반환한다. - private fun createRankResults( - order: Order, - winNumbers: WinningLotto, - ): List { - val result = groupByRanks(order, winNumbers) - return convertToRankResult(result) - } - - private fun groupByRanks( - order: Order, - winNumbers: WinningLotto, - ): Map { - return order.lottos.map { winNumbers.countMatchingNumbers(it) } - .filter { it >= Prize.RANK_RANGE.first } - .groupBy { it } - .mapValues { (_, values) -> values.size } + private fun aggregateLottoResult( + lottos: List, + winningLotto: WinningLotto, + ): List { + val result = groupByRank(lottos, winningLotto) + return result + .map { (rank, count) -> LottoResult(count, rank) } + .sortedBy { it.rank.prizeAmount } } - private fun convertToRankResult(result: Map): List { - return Prize.RANK_RANGE.map { rank -> - RankResult( - totalCount = result[rank] ?: 0, - prize = Prize.findByMatchCount(rank), - ) - } - .sortedBy { it.prize.matchCount } + private fun groupByRank( + lottos: List, + winningLotto: WinningLotto, + ): Map { + val result = Rank.entries.filter { it !== Rank.MISS }.associateWith { 0 } + val data = + lottos.map { + Rank.findByMatchCount( + winningLotto.countMatchingNumbers(it), + winningLotto.matchBonusNumber(it), + ) + } + .filter { it !== Rank.MISS } + .groupBy { it } + .mapValues { (_, values) -> values.size } + return result.mapValues { (rank, count) -> data[rank] ?: count } } } diff --git a/src/main/kotlin/lotto/view/dto/WinningResult.kt b/src/main/kotlin/lotto/view/dto/WinningResult.kt index c74ebbfe1a..6313b9d60d 100644 --- a/src/main/kotlin/lotto/view/dto/WinningResult.kt +++ b/src/main/kotlin/lotto/view/dto/WinningResult.kt @@ -1,9 +1,9 @@ package lotto.view.dto -import lotto.domain.RankResult +import lotto.domain.LottoResult data class WinningResult( - val winningMatchCounts: List, + val winningMatchCounts: List, val revenue: Int, - val rate: Double, + val rate: Int, ) diff --git a/src/test/kotlin/lotto/WinningLottoServiceTest.kt b/src/test/kotlin/lotto/WinningLottoServiceTest.kt index dce3e106c9..5073c89d7a 100644 --- a/src/test/kotlin/lotto/WinningLottoServiceTest.kt +++ b/src/test/kotlin/lotto/WinningLottoServiceTest.kt @@ -6,11 +6,22 @@ import io.kotest.matchers.shouldBe import lotto.domain.Lotto import lotto.domain.LottoNumber import lotto.domain.Order +import lotto.domain.Rank import lotto.domain.WinningLotto +import lotto.service.LottoCreator import lotto.service.WinningLottoService class WinningLottoServiceTest : StringSpec({ - "3개, 4개, 5개, 6개 에 대해 각각 몇개씩 일치하는지 정보를 제공한다." { + "입력된 숫자를 가진 당첨 로또를 생성할 수 있다." { + val lottoCreator = LottoCreator(FixedNumberGenerator()) + + val result = lottoCreator.createWinningLotto(FixedNumberGenerator().generate(), 7) + + result.winningNumbers shouldBe Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()) + result.bonusNumber shouldBe LottoNumber(7) + } + + "각 등수에 대해 각각 몇개씩 일치하는지 정보를 제공한다." { val lotto = Lotto( setOf( @@ -24,22 +35,32 @@ class WinningLottoServiceTest : StringSpec({ ) val winningLottoService = WinningLottoService() val order = Order(1000, listOf(lotto)) - val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet())) + val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) val result = winningLottoService.checkAndGetResult(order, winNumbers) assertSoftly { + result.winningMatchCounts[0].rank shouldBe Rank.FIFTH result.winningMatchCounts[0].totalCount shouldBe 1 + + result.winningMatchCounts[1].rank shouldBe Rank.FOURTH result.winningMatchCounts[1].totalCount shouldBe 0 + + result.winningMatchCounts[2].rank shouldBe Rank.THIRD result.winningMatchCounts[2].totalCount shouldBe 0 + + result.winningMatchCounts[3].rank shouldBe Rank.SECOND result.winningMatchCounts[3].totalCount shouldBe 0 + + result.winningMatchCounts[4].rank shouldBe Rank.FIRST + result.winningMatchCounts[4].totalCount shouldBe 0 } } "수익을 제공한다." { val winningLottoService = WinningLottoService() val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) - val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet())) + val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) val result = winningLottoService.checkAndGetResult(order, winNumbers) @@ -49,7 +70,7 @@ class WinningLottoServiceTest : StringSpec({ "수익률을 제공한다." { val winningLottoService = WinningLottoService() val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) - val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet())) + val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) val result = winningLottoService.checkAndGetResult(order, winNumbers) diff --git a/src/test/kotlin/lotto/WinningLottoTest.kt b/src/test/kotlin/lotto/WinningLottoTest.kt new file mode 100644 index 0000000000..86008b7434 --- /dev/null +++ b/src/test/kotlin/lotto/WinningLottoTest.kt @@ -0,0 +1,27 @@ +package lotto + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import lotto.domain.Lotto +import lotto.domain.LottoNumber +import lotto.domain.WinningLotto + +class WinningLottoTest : StringSpec({ + "일치하는 로또번호의 개수를 확인할 수 있다." { + val lotto = Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()) + val bonusNumber = LottoNumber(7) + + val winningLotto = WinningLotto(lotto, bonusNumber) + + winningLotto.countMatchingNumbers(lotto) shouldBe 6 + } + + "보너스 번호의 일치 여부를 확인할 수 있다." { + val lotto = Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()) + val bonusNumber = LottoNumber(7) + + val winningLotto = WinningLotto(lotto, bonusNumber) + + winningLotto.matchBonusNumber(lotto) shouldBe false + } +}) From 8ca43a750516c20a1c0497c1b7cd6dd3cbc6925e Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Tue, 26 Nov 2024 15:37:05 +0900 Subject: [PATCH 5/9] =?UTF-8?q?step3:=20=EB=B3=B4=EB=84=88=EC=8A=A4?= =?UTF-8?q?=EB=B3=BC=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/Main.kt | 3 ++- src/main/kotlin/lotto/const/LottoConst.kt | 4 ++++ src/main/kotlin/lotto/domain/LottoNumber.kt | 4 ++++ src/main/kotlin/lotto/domain/LottoResult.kt | 10 ++++++++++ src/main/kotlin/lotto/service/LottoCreator.kt | 7 +++++-- src/main/kotlin/lotto/view/InputView.kt | 5 +++++ src/main/kotlin/lotto/view/ResultView.kt | 12 +++++++++++- src/test/kotlin/lotto/LottoCreatorTest.kt | 10 ---------- 8 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/lotto/domain/LottoResult.kt diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index c8ca5c3846..5ae0ed7de3 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -14,7 +14,8 @@ fun main() { ResultView.printCreatedLottos(order.lottos) val winNumberInput = InputView.getWinNumberInput() - val winNumbers = winningLottoService.createWinningLotto(winNumberInput) + val bonusNumber = InputView.getBonusNumber() + val winNumbers = winningLottoService.createWinningLotto(winNumberInput, bonusNumber) val result = winningLottoService.checkAndGetResult(order, winNumbers) ResultView.printResult(result) diff --git a/src/main/kotlin/lotto/const/LottoConst.kt b/src/main/kotlin/lotto/const/LottoConst.kt index 417884f1c0..0e6871f997 100644 --- a/src/main/kotlin/lotto/const/LottoConst.kt +++ b/src/main/kotlin/lotto/const/LottoConst.kt @@ -5,4 +5,8 @@ import lotto.domain.LottoNumber object 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) }) { "일치하는 번호의 로또번호가 존재하지 않습니다." } + } } diff --git a/src/main/kotlin/lotto/domain/LottoNumber.kt b/src/main/kotlin/lotto/domain/LottoNumber.kt index 497139bb43..54db6947ac 100644 --- a/src/main/kotlin/lotto/domain/LottoNumber.kt +++ b/src/main/kotlin/lotto/domain/LottoNumber.kt @@ -10,6 +10,10 @@ value class LottoNumber(private val number: Int) { require(this.number in LOTTO_RANGE) { "로또 번호는 ${LOTTO_RANGE.first} ~ ${LOTTO_RANGE.last} 내의 숫자여야 합니다." } } + override fun toString(): String { + return "$number" + } + companion object { private val LOTTO_RANGE = 1..45 } diff --git a/src/main/kotlin/lotto/domain/LottoResult.kt b/src/main/kotlin/lotto/domain/LottoResult.kt new file mode 100644 index 0000000000..9563f15012 --- /dev/null +++ b/src/main/kotlin/lotto/domain/LottoResult.kt @@ -0,0 +1,10 @@ +package lotto.domain + +data class LottoResult( + val totalCount: Int, + val rank: Rank, +) { + fun getTotalPrizeMoney(): Int { + return this.rank.prizeAmount * totalCount + } +} diff --git a/src/main/kotlin/lotto/service/LottoCreator.kt b/src/main/kotlin/lotto/service/LottoCreator.kt index 657e38b527..476a71b626 100644 --- a/src/main/kotlin/lotto/service/LottoCreator.kt +++ b/src/main/kotlin/lotto/service/LottoCreator.kt @@ -12,9 +12,12 @@ class LottoCreator(private val numberGenerator: NumberGenerator = RandomNumberGe return List(count) { createSingleLotto() } } - fun createWinningLotto(winningNumbers: Set): WinningLotto { + fun createWinningLotto( + winningNumbers: Set, + bonusNumber: Int, + ): WinningLotto { val winningLotto = Lotto(winningNumbers.map { LottoNumber(it) }.toSet()) - return WinningLotto(winningLotto) + return WinningLotto(winningLotto, LottoConst.getLottoNumber(bonusNumber)) } private fun createSingleLotto(): Lotto { diff --git a/src/main/kotlin/lotto/view/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt index c0e10feda4..e547bd0c39 100644 --- a/src/main/kotlin/lotto/view/InputView.kt +++ b/src/main/kotlin/lotto/view/InputView.kt @@ -16,6 +16,11 @@ object InputView { .toSet() } + fun getBonusNumber(): Int { + println("보너스 볼을 입력해 주세요.") + return getNumberInput() + } + private fun getNumberInput(): Int { return requireNotNull(readln().toIntOrNull()) { "숫자를 입력해주세요." } } diff --git a/src/main/kotlin/lotto/view/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt index 6529bdd5aa..a57ca69d6c 100644 --- a/src/main/kotlin/lotto/view/ResultView.kt +++ b/src/main/kotlin/lotto/view/ResultView.kt @@ -1,6 +1,8 @@ package lotto.view import lotto.domain.Lotto +import lotto.domain.LottoResult +import lotto.domain.Rank import lotto.view.dto.WinningResult object ResultView { @@ -14,8 +16,16 @@ object ResultView { println("당첨 통계") println("-------") result.winningMatchCounts.forEach { data -> - println("${data.prize.matchCount}개 일치 (${data.prize.prizeAmount}원) - ${data.totalCount}") + printLottoResult(data) } println("총 수익률은 ${result.rate}입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)") } + + private fun printLottoResult(lottoResult: LottoResult) { + if (lottoResult.rank == Rank.SECOND) { + println("${lottoResult.rank.matchCount}개 일치, 보너스 볼 일치 (${lottoResult.rank.prizeAmount}원) - ${lottoResult.totalCount}") + } else { + println("${lottoResult.rank.matchCount}개 일치 (${lottoResult.rank.prizeAmount}원) - ${lottoResult.totalCount}") + } + } } diff --git a/src/test/kotlin/lotto/LottoCreatorTest.kt b/src/test/kotlin/lotto/LottoCreatorTest.kt index 8a13c171da..fff3bb96b4 100644 --- a/src/test/kotlin/lotto/LottoCreatorTest.kt +++ b/src/test/kotlin/lotto/LottoCreatorTest.kt @@ -2,8 +2,6 @@ package lotto import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe -import lotto.domain.Lotto -import lotto.domain.LottoNumber import lotto.service.LottoCreator class LottoCreatorTest : StringSpec({ @@ -14,12 +12,4 @@ class LottoCreatorTest : StringSpec({ result.size shouldBe 5 } - - "입력된 숫자를 가진 당첨 로또를 생성할 수 있다." { - val lottoCreator = LottoCreator(FixedNumberGenerator()) - - val result = lottoCreator.createWinningLotto(FixedNumberGenerator().generate()) - - result.winningNumbers shouldBe Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()) - } }) From 97f69a18834303c4fa794726e903e72ca525144d Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Wed, 27 Nov 2024 15:24:35 +0900 Subject: [PATCH 6/9] =?UTF-8?q?refactor:=20WinningResult=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9D=B4=EB=8F=99=20=EB=B0=8F=20=EC=B4=9D?= =?UTF-8?q?=20=EC=88=98=EC=9D=B5=EA=B3=BC=20=EC=88=98=EC=9D=B5=EB=A5=A0=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EC=B1=85=EC=9E=84=20=EB=B6=80=EC=97=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/lotto/domain/WinningResult.kt | 20 +++++++++++++++++++ src/main/kotlin/lotto/view/ResultView.kt | 2 +- .../kotlin/lotto/view/dto/WinningResult.kt | 9 --------- 3 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 src/main/kotlin/lotto/domain/WinningResult.kt delete mode 100644 src/main/kotlin/lotto/view/dto/WinningResult.kt diff --git a/src/main/kotlin/lotto/domain/WinningResult.kt b/src/main/kotlin/lotto/domain/WinningResult.kt new file mode 100644 index 0000000000..3d9a3be7b0 --- /dev/null +++ b/src/main/kotlin/lotto/domain/WinningResult.kt @@ -0,0 +1,20 @@ +package lotto.domain + +import kotlin.math.roundToInt + +class WinningResult( + val winningMatchCounts: List, + amount: Int, +) { + val revenue: Int + val rate: Int + + init { + revenue = calculateRevenue(winningMatchCounts) + rate = (revenue.toDouble() / amount.toDouble()).roundToInt() + } + + private fun calculateRevenue(matchCounts: List): Int { + return matchCounts.sumOf { it.getTotalPrizeMoney() } + } +} diff --git a/src/main/kotlin/lotto/view/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt index a57ca69d6c..41e87d0935 100644 --- a/src/main/kotlin/lotto/view/ResultView.kt +++ b/src/main/kotlin/lotto/view/ResultView.kt @@ -3,7 +3,7 @@ package lotto.view import lotto.domain.Lotto import lotto.domain.LottoResult import lotto.domain.Rank -import lotto.view.dto.WinningResult +import lotto.domain.WinningResult object ResultView { fun printCreatedLottos(lottos: List) { diff --git a/src/main/kotlin/lotto/view/dto/WinningResult.kt b/src/main/kotlin/lotto/view/dto/WinningResult.kt deleted file mode 100644 index 6313b9d60d..0000000000 --- a/src/main/kotlin/lotto/view/dto/WinningResult.kt +++ /dev/null @@ -1,9 +0,0 @@ -package lotto.view.dto - -import lotto.domain.LottoResult - -data class WinningResult( - val winningMatchCounts: List, - val revenue: Int, - val rate: Int, -) From fd8bfc20a91e2a558c0a6dcf225643267b3dc5d7 Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Wed, 27 Nov 2024 15:26:24 +0900 Subject: [PATCH 7/9] =?UTF-8?q?refactor:=20Rank=20=EA=B2=B0=EA=B3=BC?= =?UTF-8?q?=EB=A5=BC=20=EB=A7=8C=EB=93=9C=EB=8A=94=20=EC=B1=85=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20WinningLotto=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...LottoService.kt => WinningLottoMatcher.kt} | 36 ++++--------------- src/main/kotlin/lotto/domain/WinningLotto.kt | 7 ++++ 2 files changed, 14 insertions(+), 29 deletions(-) rename src/main/kotlin/lotto/{service/WinningLottoService.kt => WinningLottoMatcher.kt} (50%) diff --git a/src/main/kotlin/lotto/service/WinningLottoService.kt b/src/main/kotlin/lotto/WinningLottoMatcher.kt similarity index 50% rename from src/main/kotlin/lotto/service/WinningLottoService.kt rename to src/main/kotlin/lotto/WinningLottoMatcher.kt index 3475b1ed87..20ac605cb4 100644 --- a/src/main/kotlin/lotto/service/WinningLottoService.kt +++ b/src/main/kotlin/lotto/WinningLottoMatcher.kt @@ -1,36 +1,19 @@ -package lotto.service +package lotto import lotto.domain.Lotto import lotto.domain.LottoResult import lotto.domain.Order import lotto.domain.Rank import lotto.domain.WinningLotto -import lotto.view.dto.WinningResult -import kotlin.math.roundToInt - -class WinningLottoService { - private val lottoCreator = LottoCreator() - - fun createWinningLotto( - winningNumbers: Set, - bonusNumber: Int, - ): WinningLotto { - return lottoCreator.createWinningLotto(winningNumbers, bonusNumber) - } +import lotto.domain.WinningResult +class WinningLottoMatcher { fun checkAndGetResult( order: Order, winningLotto: WinningLotto, ): WinningResult { val winningMatchCounts = aggregateLottoResult(order.lottos, winningLotto) - val revenue = calculateRevenue(winningMatchCounts) - val rate = (revenue.toDouble() / order.amount.toDouble()).roundToInt() - - return WinningResult(winningMatchCounts, revenue, rate) - } - - private fun calculateRevenue(matchCounts: List): Int { - return matchCounts.sumOf { it.getTotalPrizeMoney() } + return WinningResult(winningMatchCounts, order.amount) } private fun aggregateLottoResult( @@ -48,16 +31,11 @@ class WinningLottoService { winningLotto: WinningLotto, ): Map { val result = Rank.entries.filter { it !== Rank.MISS }.associateWith { 0 } - val data = - lottos.map { - Rank.findByMatchCount( - winningLotto.countMatchingNumbers(it), - winningLotto.matchBonusNumber(it), - ) - } + val rankWithMatchCounts = + lottos.map { winningLotto.matchLotto(it) } .filter { it !== Rank.MISS } .groupBy { it } .mapValues { (_, values) -> values.size } - return result.mapValues { (rank, count) -> data[rank] ?: count } + return result.mapValues { (rank, count) -> rankWithMatchCounts[rank] ?: count } } } diff --git a/src/main/kotlin/lotto/domain/WinningLotto.kt b/src/main/kotlin/lotto/domain/WinningLotto.kt index b31e99f51c..5020fdeb90 100644 --- a/src/main/kotlin/lotto/domain/WinningLotto.kt +++ b/src/main/kotlin/lotto/domain/WinningLotto.kt @@ -4,6 +4,13 @@ class WinningLotto( val winningNumbers: Lotto, val bonusNumber: LottoNumber, ) { + 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 } } From 094c2793e149a857abaac6f18eb52949d18cce82 Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Wed, 27 Nov 2024 15:26:35 +0900 Subject: [PATCH 8/9] =?UTF-8?q?refactor:=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lotto/{service => }/LottoCreator.kt | 2 +- .../{service/OrderService.kt => LottoShop.kt} | 6 +-- src/main/kotlin/lotto/Main.kt | 16 ++++---- src/test/kotlin/lotto/LottoCreatorTest.kt | 1 - src/test/kotlin/lotto/LottoShopTest.kt | 23 +++++++++++ src/test/kotlin/lotto/OrderServiceTest.kt | 39 ------------------- ...viceTest.kt => WinningLottoMatcherTest.kt} | 16 ++++---- 7 files changed, 42 insertions(+), 61 deletions(-) rename src/main/kotlin/lotto/{service => }/LottoCreator.kt (97%) rename src/main/kotlin/lotto/{service/OrderService.kt => LottoShop.kt} (88%) create mode 100644 src/test/kotlin/lotto/LottoShopTest.kt delete mode 100644 src/test/kotlin/lotto/OrderServiceTest.kt rename src/test/kotlin/lotto/{WinningLottoServiceTest.kt => WinningLottoMatcherTest.kt} (84%) diff --git a/src/main/kotlin/lotto/service/LottoCreator.kt b/src/main/kotlin/lotto/LottoCreator.kt similarity index 97% rename from src/main/kotlin/lotto/service/LottoCreator.kt rename to src/main/kotlin/lotto/LottoCreator.kt index 476a71b626..ffcbb4fb92 100644 --- a/src/main/kotlin/lotto/service/LottoCreator.kt +++ b/src/main/kotlin/lotto/LottoCreator.kt @@ -1,4 +1,4 @@ -package lotto.service +package lotto import lotto.const.LottoConst import lotto.domain.Lotto diff --git a/src/main/kotlin/lotto/service/OrderService.kt b/src/main/kotlin/lotto/LottoShop.kt similarity index 88% rename from src/main/kotlin/lotto/service/OrderService.kt rename to src/main/kotlin/lotto/LottoShop.kt index 6560dd9053..4fcc6c2e3d 100644 --- a/src/main/kotlin/lotto/service/OrderService.kt +++ b/src/main/kotlin/lotto/LottoShop.kt @@ -1,11 +1,9 @@ -package lotto.service +package lotto import lotto.const.LottoConst.UNIT_OF_AMOUNT import lotto.domain.Order -class OrderService() { - private val lottoCreator = LottoCreator() - +class LottoShop(private val lottoCreator: LottoCreator) { fun makeOrder(amount: Int): Order { validateAmountIsPositive(amount) val lottoCounts = calculateLottoCounts(amount) diff --git a/src/main/kotlin/lotto/Main.kt b/src/main/kotlin/lotto/Main.kt index 5ae0ed7de3..29ef7f8834 100644 --- a/src/main/kotlin/lotto/Main.kt +++ b/src/main/kotlin/lotto/Main.kt @@ -1,22 +1,24 @@ package lotto -import lotto.service.OrderService -import lotto.service.WinningLottoService import lotto.view.InputView import lotto.view.ResultView fun main() { - val orderService = OrderService() - val winningLottoService = WinningLottoService() + val lottoCreator = LottoCreator() + val lottoShop = LottoShop(lottoCreator) + val winningLottoMatcher = WinningLottoMatcher() + // 주문 생성 val amount = InputView.getAmount() - val order = orderService.makeOrder(amount) + val order = lottoShop.makeOrder(amount) ResultView.printCreatedLottos(order.lottos) + // 당첨번호 및 보너스번호 생성 val winNumberInput = InputView.getWinNumberInput() val bonusNumber = InputView.getBonusNumber() - val winNumbers = winningLottoService.createWinningLotto(winNumberInput, bonusNumber) + val winNumbers = lottoCreator.createWinningLotto(winNumberInput, bonusNumber) - val result = winningLottoService.checkAndGetResult(order, winNumbers) + // 결과 출력 + val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) ResultView.printResult(result) } diff --git a/src/test/kotlin/lotto/LottoCreatorTest.kt b/src/test/kotlin/lotto/LottoCreatorTest.kt index fff3bb96b4..c84a6460b2 100644 --- a/src/test/kotlin/lotto/LottoCreatorTest.kt +++ b/src/test/kotlin/lotto/LottoCreatorTest.kt @@ -2,7 +2,6 @@ package lotto import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe -import lotto.service.LottoCreator class LottoCreatorTest : StringSpec({ "입력된 개수만큼의 로또를 생성할 수 있다." { diff --git a/src/test/kotlin/lotto/LottoShopTest.kt b/src/test/kotlin/lotto/LottoShopTest.kt new file mode 100644 index 0000000000..7b902decf0 --- /dev/null +++ b/src/test/kotlin/lotto/LottoShopTest.kt @@ -0,0 +1,23 @@ +package lotto + +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class LottoShopTest : StringSpec({ + "로또 금액을 입력받으면 해당 요청에 대한 주문을 생성해야한다." { + val lottoShop = LottoShop(LottoCreator(FixedNumberGenerator())) + val order = lottoShop.makeOrder(10000) + + assertSoftly { + order.amount shouldBe 10000 + order.lottos.size shouldBe 10 + } + } + + "주문 생성 시 전달된 금액이 1000원 단위가 아닐 경우 예외를 반환한다." { + val lottoShop = LottoShop(LottoCreator(FixedNumberGenerator())) + shouldThrow { lottoShop.makeOrder(10001) } + } +}) diff --git a/src/test/kotlin/lotto/OrderServiceTest.kt b/src/test/kotlin/lotto/OrderServiceTest.kt deleted file mode 100644 index 5860298859..0000000000 --- a/src/test/kotlin/lotto/OrderServiceTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package lotto - -import io.kotest.assertions.assertSoftly -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe -import lotto.service.OrderService - -class OrderServiceTest : StringSpec({ - "로또 금액을 입력받으면 해당 요청에 대한 주문을 생성해야한다." { - val orderService = OrderService() - val order = orderService.makeOrder(10000) - - assertSoftly { - order.amount shouldBe 10000 - order.lottos.size shouldBe 10 - } - } - - "주문 생성 시 전달된 금액이 1000원 단위가 아닐 경우 예외를 반환한다." { - val orderService = OrderService() - shouldThrow { orderService.makeOrder(10001) } - } - - // - // "당첨 통계 결과를 제공해야 한다." { - // val lottoSystem = LottoSystem(FixedNumberGenerator()) - // val order = lottoSystem.createOrder(1000) - // val winNumbers = lottoSystem.createWinNumbers(setOf(1, 2, 3, 8, 9, 10)) - // - // val result = lottoSystem.createWinningResult(order, winNumbers) - // - // assertSoftly { - // result.revenue shouldBe 5_000 - // result.winningMatchCounts[0].totalCount shouldBe 1 - // result.rate shouldBe 5.0 - // } - // } -}) diff --git a/src/test/kotlin/lotto/WinningLottoServiceTest.kt b/src/test/kotlin/lotto/WinningLottoMatcherTest.kt similarity index 84% rename from src/test/kotlin/lotto/WinningLottoServiceTest.kt rename to src/test/kotlin/lotto/WinningLottoMatcherTest.kt index 5073c89d7a..c49e929a9b 100644 --- a/src/test/kotlin/lotto/WinningLottoServiceTest.kt +++ b/src/test/kotlin/lotto/WinningLottoMatcherTest.kt @@ -8,10 +8,8 @@ import lotto.domain.LottoNumber import lotto.domain.Order import lotto.domain.Rank import lotto.domain.WinningLotto -import lotto.service.LottoCreator -import lotto.service.WinningLottoService -class WinningLottoServiceTest : StringSpec({ +class WinningLottoMatcherTest : StringSpec({ "입력된 숫자를 가진 당첨 로또를 생성할 수 있다." { val lottoCreator = LottoCreator(FixedNumberGenerator()) @@ -33,11 +31,11 @@ class WinningLottoServiceTest : StringSpec({ LottoNumber(9), ), ) - val winningLottoService = WinningLottoService() + val winningLottoMatcher = WinningLottoMatcher() val order = Order(1000, listOf(lotto)) val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) - val result = winningLottoService.checkAndGetResult(order, winNumbers) + val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) assertSoftly { result.winningMatchCounts[0].rank shouldBe Rank.FIFTH @@ -58,21 +56,21 @@ class WinningLottoServiceTest : StringSpec({ } "수익을 제공한다." { - val winningLottoService = WinningLottoService() + val winningLottoMatcher = WinningLottoMatcher() val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) - val result = winningLottoService.checkAndGetResult(order, winNumbers) + val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) result.revenue shouldBe 2_000_000_000 } "수익률을 제공한다." { - val winningLottoService = WinningLottoService() + val winningLottoMatcher = WinningLottoMatcher() val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) - val result = winningLottoService.checkAndGetResult(order, winNumbers) + val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) result.rate shouldBe 2000000.0 } From 82259caf873bf317a3a51915db25fc40f99c2169 Mon Sep 17 00:00:00 2001 From: SeokHo-Ham Date: Wed, 27 Nov 2024 15:39:47 +0900 Subject: [PATCH 9/9] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=9D=98=20=EA=B0=80=EB=8F=85=EC=84=B1?= =?UTF-8?q?=EC=9D=84=20=ED=96=A5=EC=83=81=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=ED=97=AC=ED=8D=BC=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/lotto/HelperFunctions.kt | 8 +++ src/test/kotlin/lotto/LottoTest.kt | 14 +----- src/test/kotlin/lotto/OrderTest.kt | 8 +-- .../kotlin/lotto/WinningLottoMatcherTest.kt | 49 +++++++------------ src/test/kotlin/lotto/WinningLottoTest.kt | 5 +- 5 files changed, 31 insertions(+), 53 deletions(-) create mode 100644 src/test/kotlin/lotto/HelperFunctions.kt diff --git a/src/test/kotlin/lotto/HelperFunctions.kt b/src/test/kotlin/lotto/HelperFunctions.kt new file mode 100644 index 0000000000..d901a4429a --- /dev/null +++ b/src/test/kotlin/lotto/HelperFunctions.kt @@ -0,0 +1,8 @@ +package lotto + +import lotto.domain.Lotto +import lotto.domain.LottoNumber + +fun createLotto(vararg numbers: Int): Lotto { + return Lotto(numbers.map { LottoNumber(it) }.toSet()) +} diff --git a/src/test/kotlin/lotto/LottoTest.kt b/src/test/kotlin/lotto/LottoTest.kt index e6e9896128..a1f8c4d1fd 100644 --- a/src/test/kotlin/lotto/LottoTest.kt +++ b/src/test/kotlin/lotto/LottoTest.kt @@ -8,19 +8,9 @@ import lotto.domain.Lotto import lotto.domain.LottoNumber class LottoTest : StringSpec({ - val correctLottoNumbers = - setOf( - LottoNumber(1), - LottoNumber(2), - LottoNumber(3), - LottoNumber(4), - LottoNumber(5), - LottoNumber(6), - ) - "한장의 로또는 6개의 숫자로 구성된다." { - val lotto = Lotto(correctLottoNumbers) + val lotto = createLotto(1, 2, 3, 4, 5, 6) lotto.numbers.size shouldBe 6 } @@ -29,7 +19,7 @@ class LottoTest : StringSpec({ assertSoftly { shouldThrow { Lotto(setOf()) } shouldThrow { Lotto(setOf(LottoNumber(1))) } - shouldThrow { Lotto(correctLottoNumbers + LottoNumber(7)) } + shouldThrow { Lotto((1..7).map { LottoNumber(it) }.toSet()) } } } }) diff --git a/src/test/kotlin/lotto/OrderTest.kt b/src/test/kotlin/lotto/OrderTest.kt index 60b53d8182..ff364ea2f9 100644 --- a/src/test/kotlin/lotto/OrderTest.kt +++ b/src/test/kotlin/lotto/OrderTest.kt @@ -41,12 +41,8 @@ class OrderTest : StringSpec({ Order( 10000, listOf( - Lotto( - setOf( - LottoNumber(1), - LottoNumber(2), - ), - ), + createLotto(1, 2, 3, 4, 5, 6), + createLotto(1, 2, 3, 7, 8, 9), ), ) } diff --git a/src/test/kotlin/lotto/WinningLottoMatcherTest.kt b/src/test/kotlin/lotto/WinningLottoMatcherTest.kt index c49e929a9b..8994c7eebb 100644 --- a/src/test/kotlin/lotto/WinningLottoMatcherTest.kt +++ b/src/test/kotlin/lotto/WinningLottoMatcherTest.kt @@ -1,6 +1,5 @@ package lotto -import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import lotto.domain.Lotto @@ -20,45 +19,31 @@ class WinningLottoMatcherTest : StringSpec({ } "각 등수에 대해 각각 몇개씩 일치하는지 정보를 제공한다." { - val lotto = - Lotto( - setOf( - LottoNumber(1), - LottoNumber(2), - LottoNumber(3), - LottoNumber(7), - LottoNumber(8), - LottoNumber(9), - ), - ) + val lotto = createLotto(1, 2, 3, 7, 8, 9) val winningLottoMatcher = WinningLottoMatcher() val order = Order(1000, listOf(lotto)) - val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) + val winNumbers = WinningLotto(createLotto(1, 2, 3, 4, 5, 6), LottoNumber(7)) val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) + val expected = + listOf( + Rank.FIFTH to 1, + Rank.FOURTH to 0, + Rank.THIRD to 0, + Rank.SECOND to 0, + Rank.FIRST to 0, + ) - assertSoftly { - result.winningMatchCounts[0].rank shouldBe Rank.FIFTH - result.winningMatchCounts[0].totalCount shouldBe 1 - - result.winningMatchCounts[1].rank shouldBe Rank.FOURTH - result.winningMatchCounts[1].totalCount shouldBe 0 - - result.winningMatchCounts[2].rank shouldBe Rank.THIRD - result.winningMatchCounts[2].totalCount shouldBe 0 - - result.winningMatchCounts[3].rank shouldBe Rank.SECOND - result.winningMatchCounts[3].totalCount shouldBe 0 - - result.winningMatchCounts[4].rank shouldBe Rank.FIRST - result.winningMatchCounts[4].totalCount shouldBe 0 + expected.forEachIndexed { index, pair -> + pair.first shouldBe result.winningMatchCounts[index].rank + pair.second shouldBe result.winningMatchCounts[index].totalCount } } "수익을 제공한다." { val winningLottoMatcher = WinningLottoMatcher() - val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) - val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) + val order = Order(1000, listOf(createLotto(1, 2, 3, 4, 5, 6))) + val winNumbers = WinningLotto(createLotto(1, 2, 3, 4, 5, 6), LottoNumber(7)) val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) @@ -67,8 +52,8 @@ class WinningLottoMatcherTest : StringSpec({ "수익률을 제공한다." { val winningLottoMatcher = WinningLottoMatcher() - val order = Order(1000, listOf(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()))) - val winNumbers = WinningLotto(Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()), LottoNumber(7)) + val order = Order(1000, listOf(createLotto(1, 2, 3, 4, 5, 6))) + val winNumbers = WinningLotto(createLotto(1, 2, 3, 4, 5, 6), LottoNumber(7)) val result = winningLottoMatcher.checkAndGetResult(order, winNumbers) diff --git a/src/test/kotlin/lotto/WinningLottoTest.kt b/src/test/kotlin/lotto/WinningLottoTest.kt index 86008b7434..20d52d466c 100644 --- a/src/test/kotlin/lotto/WinningLottoTest.kt +++ b/src/test/kotlin/lotto/WinningLottoTest.kt @@ -2,13 +2,12 @@ package lotto import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe -import lotto.domain.Lotto import lotto.domain.LottoNumber import lotto.domain.WinningLotto class WinningLottoTest : StringSpec({ "일치하는 로또번호의 개수를 확인할 수 있다." { - val lotto = Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()) + val lotto = createLotto(1, 2, 3, 4, 5, 6) val bonusNumber = LottoNumber(7) val winningLotto = WinningLotto(lotto, bonusNumber) @@ -17,7 +16,7 @@ class WinningLottoTest : StringSpec({ } "보너스 번호의 일치 여부를 확인할 수 있다." { - val lotto = Lotto(FixedNumberGenerator().generate().map { LottoNumber(it) }.toSet()) + val lotto = createLotto(1, 2, 3, 4, 5, 6) val bonusNumber = LottoNumber(7) val winningLotto = WinningLotto(lotto, bonusNumber)