From 5bb9b489e74e0f8bf7b72239400e62363172f771 Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Sat, 17 Feb 2024 16:38:46 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20DI=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20-=201=EB=8B=A8=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.name | 1 + .idea/compiler.xml | 6 ++++ kotlin-lotto/build.gradle.kts | 2 ++ .../src/main/kotlin/lotto/Application.kt | 2 +- .../lotto/global/DependencyInjection.kt | 31 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..35fe63d --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +kotlin-baseball \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/kotlin-lotto/build.gradle.kts b/kotlin-lotto/build.gradle.kts index c006c73..05763c2 100644 --- a/kotlin-lotto/build.gradle.kts +++ b/kotlin-lotto/build.gradle.kts @@ -9,6 +9,8 @@ repositories { dependencies { implementation("com.github.woowacourse-projects:mission-utils:1.1.0") + implementation("org.jetbrains.kotlin-reflection:1.9.0") + implementation(kotlin("reflect")) } java { diff --git a/kotlin-lotto/src/main/kotlin/lotto/Application.kt b/kotlin-lotto/src/main/kotlin/lotto/Application.kt index d716876..b0a817a 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/Application.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/Application.kt @@ -1,5 +1,5 @@ package lotto fun main() { - TODO("프로그램 구현") + } diff --git a/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt new file mode 100644 index 0000000..5dddf06 --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt @@ -0,0 +1,31 @@ +package lotto.global + +import kotlin.reflect.KClass + +object ContainerV1 { + // 등록한 클래스를 보관! = KClass를 보관 + + private val registeredClasses = mutableSetOf>() + + fun register(clazz: KClass<*>) { + registeredClasses.add(clazz) + } + + fun getInstance(type: KClass) : T = + registeredClasses.firstOrNull { clazz -> clazz == type} + ?.let { clazz -> clazz.constructors.first().call() as T } + ?: throw IllegalArgumentException("해당 인스턴스 타입을 찾을 수 없습니다.") + +} + +fun main() { + ContainerV1.register(AService::class) + val aService = ContainerV1.getInstance(AService::class) + aService.print() +} + +class AService { + fun print() { + println("A Service 입니다") + } +} \ No newline at end of file From f7282b29ed62f928424f940c8ec9fa60c9e66af1 Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Sat, 17 Feb 2024 17:22:03 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20DI=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20-=202=EB=8B=A8=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kotlin-lotto/build.gradle.kts | 1 + .../lotto/global/DependencyInjection.kt | 75 ++++++++++++++++++- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/kotlin-lotto/build.gradle.kts b/kotlin-lotto/build.gradle.kts index 05763c2..67f05b1 100644 --- a/kotlin-lotto/build.gradle.kts +++ b/kotlin-lotto/build.gradle.kts @@ -9,6 +9,7 @@ repositories { dependencies { implementation("com.github.woowacourse-projects:mission-utils:1.1.0") + implementation("org.reflections:reflections:0.10.2") implementation("org.jetbrains.kotlin-reflection:1.9.0") implementation(kotlin("reflect")) } diff --git a/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt index 5dddf06..1ce2bd5 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt @@ -1,6 +1,9 @@ package lotto.global import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KParameter +import kotlin.reflect.cast object ContainerV1 { // 등록한 클래스를 보관! = KClass를 보관 @@ -18,14 +21,78 @@ object ContainerV1 { } +object ContainerV2 { + // 등록한 클래스를 보관! = KClass를 보관 + + private val registeredClasses = mutableSetOf>() + private val cachedInstances = mutableMapOf, Any>() + + fun register(clazz: KClass<*>) { + registeredClasses.add(clazz) + } + + fun getInstance(type: KClass) : T { + if (type in cachedInstances) { + return type.cast(cachedInstances[type]) + } + + val instance = registeredClasses.firstOrNull { clazz -> clazz == type } + ?.let { clazz -> instantiate(clazz) as T } + ?: throw IllegalArgumentException("해당 인스턴스 타입을 찾을 수 없습니다.") + + cachedInstances[type] = instance + return instance + } + + private fun instantiate(clazz: KClass): T { + val constructor = findUsableConstructor(clazz) + val params = constructor.parameters + .map { param -> getInstance(param.type.classifier as KClass<*>) } + .toTypedArray() + + return constructor.call(*params) + } + + // clazz의 constructor 들 중, 사용할 수 있는 constructor + // constructor 에 넣어야 하는 타입들이 모두 등록된 경우(컨테이너에서 관리하고 있는 경우를 의미) + + private fun findUsableConstructor(clazz: KClass): KFunction = + clazz.constructors.firstOrNull { constructor -> constructor.parameters.isAllRegistered } + ?: throw IllegalArgumentException("사용할 수 있는 생성자가 없습니다") + + private val List.isAllRegistered: Boolean + get() = this.all { it.type.classifier in registeredClasses } + +} + fun main() { - ContainerV1.register(AService::class) - val aService = ContainerV1.getInstance(AService::class) - aService.print() +// ContainerV1.register(AService::class) +// val aService = ContainerV1.getInstance(AService::class) +// aService.print() + + ContainerV2.register(AService::class) + ContainerV2.register(BService::class) + + val bService = ContainerV2.getInstance(BService::class) + bService.print() } class AService { fun print() { println("A Service 입니다") } -} \ No newline at end of file +} + +class BService ( + private val aService: AService, + private val cService: CService?, +) { + + constructor(aService: AService): this(aService, null) + + fun print() { + this.aService.print() + } +} + +class CService \ No newline at end of file From 60a847fb839ea3b9a4ca522915c624eab982f04c Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Sun, 18 Feb 2024 15:50:52 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20DI=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/lotto/Application.kt | 8 +++++++ kotlin-lotto/src/main/kotlin/lotto/Lotto.kt | 7 ++++++ .../src/main/kotlin/lotto/global/Component.kt | 4 ++++ .../lotto/global/DependencyInjection.kt | 24 +++++++++++-------- 4 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 kotlin-lotto/src/main/kotlin/lotto/global/Component.kt diff --git a/kotlin-lotto/src/main/kotlin/lotto/Application.kt b/kotlin-lotto/src/main/kotlin/lotto/Application.kt index b0a817a..f5a1e4f 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/Application.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/Application.kt @@ -1,5 +1,13 @@ package lotto +import lotto.global.BService +import lotto.global.ContainerV2 +import lotto.global.start + fun main() { + start(Lotto::class) + val instance = ContainerV2.getInstance(Lotto::class) + instance.printHello() + } diff --git a/kotlin-lotto/src/main/kotlin/lotto/Lotto.kt b/kotlin-lotto/src/main/kotlin/lotto/Lotto.kt index 5ca00b4..5dece20 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/Lotto.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/Lotto.kt @@ -1,9 +1,16 @@ package lotto +import lotto.global.Component + +@Component class Lotto(private val numbers: List) { init { require(numbers.size == 6) } // TODO: 추가 기능 구현 + + fun printHello() { + println("HI") + } } diff --git a/kotlin-lotto/src/main/kotlin/lotto/global/Component.kt b/kotlin-lotto/src/main/kotlin/lotto/global/Component.kt new file mode 100644 index 0000000..9e5169b --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/global/Component.kt @@ -0,0 +1,4 @@ +package lotto.global + +annotation class Component + diff --git a/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt index 1ce2bd5..275fdd0 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt @@ -1,5 +1,6 @@ package lotto.global +import org.reflections.Reflections import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter @@ -65,24 +66,27 @@ object ContainerV2 { } -fun main() { -// ContainerV1.register(AService::class) -// val aService = ContainerV1.getInstance(AService::class) -// aService.print() - - ContainerV2.register(AService::class) - ContainerV2.register(BService::class) - - val bService = ContainerV2.getInstance(BService::class) - bService.print() +fun start(clazz: KClass<*>) { + val reflections = Reflections(clazz.packageName) + val jClasses = reflections.getTypesAnnotatedWith(Component::class.java) + jClasses.forEach { jClasses -> ContainerV2.register(jClasses.kotlin) } } +private val KClass<*>.packageName: String + get() { + val qualifiedName = this.qualifiedName ?: throw IllegalArgumentException("익명 객체입니다!") + val hierarchy = qualifiedName.split(".") + return hierarchy.subList(0, hierarchy.lastIndex).joinToString(".") + } + +@Component class AService { fun print() { println("A Service 입니다") } } +@Component class BService ( private val aService: AService, private val cService: CService?, From 362327eb569ff87c08cd1e2bcc98ee375def0009 Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Wed, 21 Feb 2024 01:46:56 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=201=EC=B0=A8=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/lotto/Application.kt | 16 +++--- kotlin-lotto/src/main/kotlin/lotto/Lotto.kt | 16 ------ .../lotto/controller/LottoController.kt | 52 +++++++++++++++++++ .../lotto/global/DependencyInjection.kt | 46 ++-------------- .../src/main/kotlin/lotto/model/Lotto.kt | 26 ++++++++++ .../src/main/kotlin/lotto/model/LottoBuyer.kt | 16 ++++++ .../kotlin/lotto/model/LottoGameManager.kt | 36 +++++++++++++ .../kotlin/lotto/model/LottoWinningRank.kt | 21 ++++++++ .../kotlin/lotto/view/LottoGameMessageView.kt | 37 +++++++++++++ .../src/test/kotlin/lotto/LottoTest.kt | 1 + 10 files changed, 202 insertions(+), 65 deletions(-) delete mode 100644 kotlin-lotto/src/main/kotlin/lotto/Lotto.kt create mode 100644 kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt create mode 100644 kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt create mode 100644 kotlin-lotto/src/main/kotlin/lotto/model/LottoBuyer.kt create mode 100644 kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt create mode 100644 kotlin-lotto/src/main/kotlin/lotto/model/LottoWinningRank.kt create mode 100644 kotlin-lotto/src/main/kotlin/lotto/view/LottoGameMessageView.kt diff --git a/kotlin-lotto/src/main/kotlin/lotto/Application.kt b/kotlin-lotto/src/main/kotlin/lotto/Application.kt index f5a1e4f..4bd3b79 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/Application.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/Application.kt @@ -1,13 +1,17 @@ package lotto -import lotto.global.BService -import lotto.global.ContainerV2 -import lotto.global.start +import lotto.controller.LottoController +import lotto.global.DIContainer +import lotto.global.componentScan +import lotto.model.LottoGameManager +import lotto.view.LottoGameMessageView + +class Application fun main() { + componentScan(Application::class) - start(Lotto::class) - val instance = ContainerV2.getInstance(Lotto::class) - instance.printHello() + val lottoGameManager = DIContainer.getInstance(LottoGameManager::class) + lottoGameManager.run() } diff --git a/kotlin-lotto/src/main/kotlin/lotto/Lotto.kt b/kotlin-lotto/src/main/kotlin/lotto/Lotto.kt deleted file mode 100644 index 5dece20..0000000 --- a/kotlin-lotto/src/main/kotlin/lotto/Lotto.kt +++ /dev/null @@ -1,16 +0,0 @@ -package lotto - -import lotto.global.Component - -@Component -class Lotto(private val numbers: List) { - init { - require(numbers.size == 6) - } - - // TODO: 추가 기능 구현 - - fun printHello() { - println("HI") - } -} diff --git a/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt b/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt new file mode 100644 index 0000000..401e927 --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt @@ -0,0 +1,52 @@ +package lotto.controller + +import camp.nextstep.edu.missionutils.Console +import camp.nextstep.edu.missionutils.Randoms +import lotto.global.Component +import lotto.model.Lotto +import lotto.model.LottoBuyer +import lotto.model.LottoWinningRank +import lotto.view.LottoGameMessageView + +@Component +class LottoController( + private val lottoGameMessageView: LottoGameMessageView +) { + + fun purchaseLotto(): Int { + lottoGameMessageView.announcePurchaseMessage() + return (Console.readLine() + ?.toIntOrNull() + ?: IllegalArgumentException("올바른 구입금액을 입력해 주세요.")) as Int + } + + fun enterWinningNumbers(): List { + lottoGameMessageView.announceEnterWinningNumbers() + return (Console.readLine() + ?.split(",") + ?.map { it.toInt() } + ?: throw IllegalArgumentException("올바른 당첨 번호를 입력해 주세요.")) + } + + fun enterBonusNumber(): Int { + lottoGameMessageView.announceEnterBonusNumbers() + return Console.readLine() + ?.toIntOrNull() + ?: throw IllegalArgumentException("올바른 보너스 번호를 입력해 주세요.") + } + + fun announceResult(lottoResult: List, purchaseAmount: Int) { + lottoGameMessageView.announceLottoResult(lottoResult, purchaseAmount) + } + + fun announceLottoNumbers(purchaseAmount: Int): List { + val randomLottos = List(purchaseAmount / 1000) { Lotto(generateRandomLottoNumbers()) } + lottoGameMessageView.announcePurchasedLottos(randomLottos) + return randomLottos + } + + private fun generateRandomLottoNumbers(): List = + Randoms.pickUniqueNumbersInRange(Lotto.LOTTO_START_NUMBER, Lotto.LOTTO_END_NUMBER, Lotto.LOTTO_TOTAL_COUNT) + + +} \ No newline at end of file diff --git a/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt index 275fdd0..52d0dec 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/global/DependencyInjection.kt @@ -6,25 +6,8 @@ import kotlin.reflect.KFunction import kotlin.reflect.KParameter import kotlin.reflect.cast -object ContainerV1 { - // 등록한 클래스를 보관! = KClass를 보관 - - private val registeredClasses = mutableSetOf>() - - fun register(clazz: KClass<*>) { - registeredClasses.add(clazz) - } - - fun getInstance(type: KClass) : T = - registeredClasses.firstOrNull { clazz -> clazz == type} - ?.let { clazz -> clazz.constructors.first().call() as T } - ?: throw IllegalArgumentException("해당 인스턴스 타입을 찾을 수 없습니다.") - -} - -object ContainerV2 { - // 등록한 클래스를 보관! = KClass를 보관 +object DIContainer { private val registeredClasses = mutableSetOf>() private val cachedInstances = mutableMapOf, Any>() @@ -56,7 +39,6 @@ object ContainerV2 { // clazz의 constructor 들 중, 사용할 수 있는 constructor // constructor 에 넣어야 하는 타입들이 모두 등록된 경우(컨테이너에서 관리하고 있는 경우를 의미) - private fun findUsableConstructor(clazz: KClass): KFunction = clazz.constructors.firstOrNull { constructor -> constructor.parameters.isAllRegistered } ?: throw IllegalArgumentException("사용할 수 있는 생성자가 없습니다") @@ -66,10 +48,10 @@ object ContainerV2 { } -fun start(clazz: KClass<*>) { +fun componentScan(clazz: KClass<*>) { val reflections = Reflections(clazz.packageName) val jClasses = reflections.getTypesAnnotatedWith(Component::class.java) - jClasses.forEach { jClasses -> ContainerV2.register(jClasses.kotlin) } + jClasses.forEach { jClasses -> DIContainer.register(jClasses.kotlin) } } private val KClass<*>.packageName: String @@ -78,25 +60,3 @@ private val KClass<*>.packageName: String val hierarchy = qualifiedName.split(".") return hierarchy.subList(0, hierarchy.lastIndex).joinToString(".") } - -@Component -class AService { - fun print() { - println("A Service 입니다") - } -} - -@Component -class BService ( - private val aService: AService, - private val cService: CService?, -) { - - constructor(aService: AService): this(aService, null) - - fun print() { - this.aService.print() - } -} - -class CService \ No newline at end of file diff --git a/kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt b/kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt new file mode 100644 index 0000000..13cd19e --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt @@ -0,0 +1,26 @@ +package lotto.model + +import lotto.global.Component + +class Lotto(private val numbers: List) { + init { + require(numbers.size == 6) + } + + companion object { + const val LOTTO_START_NUMBER = 1 + const val LOTTO_END_NUMBER = 45 + const val LOTTO_TOTAL_COUNT = 6 + } + + fun count(winningNumbers: List): Int = + (numbers intersect winningNumbers.toSet()).size + + + fun count(bonusNumber: Int): Int = + if (numbers.contains(bonusNumber)) 1 else 0 + + override fun toString(): String = + numbers.toString() + +} diff --git a/kotlin-lotto/src/main/kotlin/lotto/model/LottoBuyer.kt b/kotlin-lotto/src/main/kotlin/lotto/model/LottoBuyer.kt new file mode 100644 index 0000000..6a37c81 --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/model/LottoBuyer.kt @@ -0,0 +1,16 @@ +package lotto.model + +class LottoBuyer( + private val purchasedLottos : List, + private val winningNumbers: List, + private val bonusNumber: Int +) { + + fun compareLottoNumbers(): List = + purchasedLottos.map { lotto -> + LottoWinningRank.entries.firstOrNull { rank -> + rank.isRankMatch(lotto.count(winningNumbers), lotto.count(bonusNumber)) + } + } + +} diff --git a/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt b/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt new file mode 100644 index 0000000..612148e --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt @@ -0,0 +1,36 @@ +package lotto.model + +import camp.nextstep.edu.missionutils.Randoms +import lotto.controller.LottoController +import lotto.global.Component + +@Component +class LottoGameManager( + private val lottoController: LottoController +) { + + fun run() { + // STEP1 > 구입금액 입력 받기 + val purchaseAmount = lottoController.purchaseLotto() + + val randomLottos = lottoController.announceLottoNumbers(purchaseAmount) + + // STEP2 > 당첨 번호 입력 받기 + val winningNumbers = lottoController.enterWinningNumbers() + + // STEP3 > 보너스 번호 입력 받기 + val bonusNumber = lottoController.enterBonusNumber() + + // STEP4 > 로또 당첨 내역 출력 + val lottoBuyer = LottoBuyer( + randomLottos, + winningNumbers, + bonusNumber + ) + + val lottoResultList = lottoBuyer.compareLottoNumbers() + lottoController.announceResult(lottoResultList, purchaseAmount) + } + + +} \ No newline at end of file diff --git a/kotlin-lotto/src/main/kotlin/lotto/model/LottoWinningRank.kt b/kotlin-lotto/src/main/kotlin/lotto/model/LottoWinningRank.kt new file mode 100644 index 0000000..5bf9e2e --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/model/LottoWinningRank.kt @@ -0,0 +1,21 @@ +package lotto.model + +import java.util.function.BiFunction + +enum class LottoWinningRank( + val matchCount: Int, + val bonusCount: Int, + val lottoPrice: Long, + val isWinningRank: (matchCount: Int, bonusCount: Int) -> Boolean +) { + + FIRST_PLACE(6, 0, 2_000_000_000, { matchCount, bonusCount -> matchCount == 6 }), + SECOND_PLACE(5, 1, 30_000_000, { matchCount, bonusCount -> matchCount == 5 && bonusCount == 1 }), + THIRD_PLACE(5, 0, 1_500_000, { matchCount, bonusCount -> matchCount == 5 && bonusCount == 0 }), + FOURTH_PLACE(4, 0, 50_000, { matchCount, bonusCount -> matchCount == 4}), + FIFTH_PLACE(3, 0, 5_000, { matchCount, bonusCount -> matchCount == 3 }), + ; + + fun isRankMatch(matchCount: Int, bonusCount: Int) = + isWinningRank(matchCount, bonusCount) +} \ No newline at end of file diff --git a/kotlin-lotto/src/main/kotlin/lotto/view/LottoGameMessageView.kt b/kotlin-lotto/src/main/kotlin/lotto/view/LottoGameMessageView.kt new file mode 100644 index 0000000..4875370 --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/view/LottoGameMessageView.kt @@ -0,0 +1,37 @@ +package lotto.view + +import lotto.model.Lotto +import lotto.global.Component +import lotto.model.LottoWinningRank + +@Component +class LottoGameMessageView { + + fun announcePurchaseMessage() = println("구입금액을 입력해 주세요.") + + fun announcePurchasedLottos(purchasedLottos: List) { + println("\n${purchasedLottos.size}개를 구매했습니다.") + purchasedLottos.forEach{ println(it) } + } + + fun announceEnterWinningNumbers() = println("\n당첨 번호를 입력해 주세요.") + + fun announceEnterBonusNumbers() = println("\n보너스 번호를 입력해 주세요.") + + fun announceLottoResult(lottoResultDto: List, purchaseAmount: Int) = + println(""" + + 당첨 통계 + --- + 3개 일치 (5,000원) - ${lottoResultDto.count { it == LottoWinningRank.FIFTH_PLACE}}개 + 4개 일치 (50,000원) - ${lottoResultDto.count { it == LottoWinningRank.FOURTH_PLACE }}개 + 5개 일치 (1,500,000원) - ${lottoResultDto.count { it == LottoWinningRank.THIRD_PLACE }}개 + 5개 일치, 보너스 볼 일치 (30,000,000원) - ${lottoResultDto.count { it == LottoWinningRank.SECOND_PLACE }}개 + 6개 일치 (2,000,000,000원) - ${lottoResultDto.count { it == LottoWinningRank.FIRST_PLACE }}개 + 총 수익률은 ${"%.1f".format(calculateTotalRateOfReturn(lottoResultDto, purchaseAmount))}%입니다. + """.trimIndent()) + + private fun calculateTotalRateOfReturn(lottoResultDto: List, purchaseAmount: Int): Double = + lottoResultDto.sumOf { it?.lottoPrice ?: 0L }.toDouble() / purchaseAmount * 100 + +} \ No newline at end of file diff --git a/kotlin-lotto/src/test/kotlin/lotto/LottoTest.kt b/kotlin-lotto/src/test/kotlin/lotto/LottoTest.kt index 11d85ac..666e080 100644 --- a/kotlin-lotto/src/test/kotlin/lotto/LottoTest.kt +++ b/kotlin-lotto/src/test/kotlin/lotto/LottoTest.kt @@ -1,5 +1,6 @@ package lotto +import lotto.model.Lotto import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows From 94564218579df55b07d58870f1c1f2dc3fa26f31 Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Wed, 21 Feb 2024 02:25:21 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat:=202=EC=B0=A8=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lotto/controller/LottoController.kt | 24 +++++++++++++------ .../src/main/kotlin/lotto/model/Lotto.kt | 1 + 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt b/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt index 401e927..a80ebc5 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt @@ -15,24 +15,35 @@ class LottoController( fun purchaseLotto(): Int { lottoGameMessageView.announcePurchaseMessage() - return (Console.readLine() - ?.toIntOrNull() - ?: IllegalArgumentException("올바른 구입금액을 입력해 주세요.")) as Int + while (true) { + try { + return (Console.readLine() + ?.toIntOrNull() + ?: IllegalArgumentException("[ERROR] 올바른 구입금액을 입력해 주세요.")) as Int + } catch (e: IllegalArgumentException) { + println(e.message) + } catch (e: ClassCastException) { + println("[ERROR] 올바른 구매금액을 입력해주세요.") + } + } } fun enterWinningNumbers(): List { lottoGameMessageView.announceEnterWinningNumbers() - return (Console.readLine() + val winningNumbers = (Console.readLine() ?.split(",") ?.map { it.toInt() } - ?: throw IllegalArgumentException("올바른 당첨 번호를 입력해 주세요.")) + ?: throw IllegalArgumentException("[ERROR] 올바른 당첨 번호를 입력해 주세요.")) + + return winningNumbers + } fun enterBonusNumber(): Int { lottoGameMessageView.announceEnterBonusNumbers() return Console.readLine() ?.toIntOrNull() - ?: throw IllegalArgumentException("올바른 보너스 번호를 입력해 주세요.") + ?: throw IllegalArgumentException("[ERROR] 올바른 보너스 번호를 입력해 주세요.") } fun announceResult(lottoResult: List, purchaseAmount: Int) { @@ -48,5 +59,4 @@ class LottoController( private fun generateRandomLottoNumbers(): List = Randoms.pickUniqueNumbersInRange(Lotto.LOTTO_START_NUMBER, Lotto.LOTTO_END_NUMBER, Lotto.LOTTO_TOTAL_COUNT) - } \ No newline at end of file diff --git a/kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt b/kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt index 13cd19e..a4c8a3c 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/model/Lotto.kt @@ -5,6 +5,7 @@ import lotto.global.Component class Lotto(private val numbers: List) { init { require(numbers.size == 6) + require(numbers.distinct().size == numbers.size) } companion object { From a1f71515ab9a2db3344f051e0a7d32af3af4c15f Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Wed, 21 Feb 2024 02:27:40 +0900 Subject: [PATCH 6/9] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/lotto/model/LottoGameManager.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt b/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt index 612148e..61f19d2 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt @@ -13,15 +13,16 @@ class LottoGameManager( // STEP1 > 구입금액 입력 받기 val purchaseAmount = lottoController.purchaseLotto() + // STEP2 > 구매 내역 출력 val randomLottos = lottoController.announceLottoNumbers(purchaseAmount) - // STEP2 > 당첨 번호 입력 받기 + // STEP3 > 당첨 번호 입력 받기 val winningNumbers = lottoController.enterWinningNumbers() - // STEP3 > 보너스 번호 입력 받기 + // STEP4 > 보너스 번호 입력 받기 val bonusNumber = lottoController.enterBonusNumber() - // STEP4 > 로또 당첨 내역 출력 + // STEP5 > 로또 당첨 내역 출력 val lottoBuyer = LottoBuyer( randomLottos, winningNumbers, From a1b9c52757a00176aefcca6c1c2a17e549250a21 Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Wed, 21 Feb 2024 02:41:36 +0900 Subject: [PATCH 7/9] =?UTF-8?q?chore:=20=EA=B0=9C=ED=96=89=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=EC=9D=B8=EB=9D=BC=EC=9D=B8=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/lotto/controller/LottoController.kt | 4 +--- kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt b/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt index a80ebc5..f4435aa 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/controller/LottoController.kt @@ -30,13 +30,11 @@ class LottoController( fun enterWinningNumbers(): List { lottoGameMessageView.announceEnterWinningNumbers() - val winningNumbers = (Console.readLine() + return (Console.readLine() ?.split(",") ?.map { it.toInt() } ?: throw IllegalArgumentException("[ERROR] 올바른 당첨 번호를 입력해 주세요.")) - return winningNumbers - } fun enterBonusNumber(): Int { diff --git a/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt b/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt index 61f19d2..911643d 100644 --- a/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt +++ b/kotlin-lotto/src/main/kotlin/lotto/model/LottoGameManager.kt @@ -33,5 +33,4 @@ class LottoGameManager( lottoController.announceResult(lottoResultList, purchaseAmount) } - } \ No newline at end of file From 09f6d4541705b4c89f0272270fc2a93cd4d2e673 Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Mon, 4 Mar 2024 16:03:45 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20=EC=82=B0=ED=83=80=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=201=EC=B0=A8=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kotlin-christmas/build.gradle.kts | 3 + .../src/main/kotlin/christmas/Application.kt | 8 ++- .../kotlin/christmas/ChristmasGameManager.kt | 29 +++++++++ .../ChristmasDdayDiscountPolicy.kt | 17 +++++ .../discount_policy/EventDiscountPolicy.kt | 13 ++++ .../EventDiscountPolicyFactory.kt | 6 ++ .../discount_policy/SpecialDiscountPolicy.kt | 20 ++++++ .../discount_policy/WeekdayDiscountPolicy.kt | 23 +++++++ .../discount_policy/WeekendDiscountPolicy.kt | 24 +++++++ .../src/main/kotlin/christmas/domain/Badge.kt | 13 ++++ .../src/main/kotlin/christmas/domain/Menu.kt | 25 ++++++++ .../main/kotlin/christmas/domain/MenuType.kt | 9 +++ .../main/kotlin/christmas/dto/OrderMenuDto.kt | 41 ++++++++++++ .../main/kotlin/christmas/global/Component.kt | 3 + .../main/kotlin/christmas/global/Container.kt | 62 +++++++++++++++++++ .../main/kotlin/christmas/view/InputView.kt | 58 +++++++++++++++++ .../main/kotlin/christmas/view/OutputView.kt | 58 +++++++++++++++++ .../main/kotlin/lotto/dsl/dockerCompose.kt | 2 + 18 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 kotlin-christmas/src/main/kotlin/christmas/ChristmasGameManager.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicyFactory.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/domain/Badge.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/domain/Menu.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/domain/MenuType.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/dto/OrderMenuDto.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/global/Component.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/global/Container.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt create mode 100644 kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt create mode 100644 kotlin-lotto/src/main/kotlin/lotto/dsl/dockerCompose.kt diff --git a/kotlin-christmas/build.gradle.kts b/kotlin-christmas/build.gradle.kts index c006c73..67f05b1 100644 --- a/kotlin-christmas/build.gradle.kts +++ b/kotlin-christmas/build.gradle.kts @@ -9,6 +9,9 @@ repositories { dependencies { implementation("com.github.woowacourse-projects:mission-utils:1.1.0") + implementation("org.reflections:reflections:0.10.2") + implementation("org.jetbrains.kotlin-reflection:1.9.0") + implementation(kotlin("reflect")) } java { diff --git a/kotlin-christmas/src/main/kotlin/christmas/Application.kt b/kotlin-christmas/src/main/kotlin/christmas/Application.kt index 8d101ce..cc5b230 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/Application.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/Application.kt @@ -1,5 +1,11 @@ package christmas +import christmas.global.Container + +class Application + fun main() { - TODO("프로그램 구현") + Container.componentScan(Application::class) + val christmasGameManager = Container.getInstance(ChristmasGameManager::class) + christmasGameManager.run() } diff --git a/kotlin-christmas/src/main/kotlin/christmas/ChristmasGameManager.kt b/kotlin-christmas/src/main/kotlin/christmas/ChristmasGameManager.kt new file mode 100644 index 0000000..d17c7cd --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/ChristmasGameManager.kt @@ -0,0 +1,29 @@ +package christmas + +import christmas.global.Component +import christmas.view.InputView +import christmas.view.OutputView + +@Component +class ChristmasGameManager( + private val inputView: InputView, + private val outputView: OutputView, +) { + + fun run() { + // STEP1 > 식당 예상 방문 날짜 입력 받기 + outputView.printEventMessage() + val userVisitDay = inputView.getVisitDay() + + // STEP2 > 주문할 메뉴와 개수 입력 받기 + outputView.printMenuAndCountMessage() + val ordersDto = inputView.getOrderMenuAndCount(userVisitDay) + + // STEP3 > 이벤트 혜택 미리보기 출력 + outputView.printEventListMessage(userVisitDay) + + // STEP4 > 주문 메뉴 출력 + outputView.printReceipt(ordersDto) + } + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt new file mode 100644 index 0000000..3d743f1 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt @@ -0,0 +1,17 @@ +package christmas.discount_policy + +import christmas.dto.OrderMenuDto +import christmas.global.Component + +@Component +class ChristmasDdayDiscountPolicy: EventDiscountPolicy { + + override val discountPolicyName: String + get() = "크리스마스 디데이 할인" + + override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean = + orderMenuDto.reservationDate in 1..25 + + override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = + 1000L + 100L * (orderMenuDto.reservationDate - 1) +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt new file mode 100644 index 0000000..f277089 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt @@ -0,0 +1,13 @@ +package christmas.discount_policy + +import christmas.dto.OrderMenuDto + +interface EventDiscountPolicy { + + val discountPolicyName: String + + fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean + + fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicyFactory.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicyFactory.kt new file mode 100644 index 0000000..500d011 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicyFactory.kt @@ -0,0 +1,6 @@ +package christmas.discount_policy + +fun createEventDiscountPolicy(): List = + listOf(ChristmasDdayDiscountPolicy(), SpecialDiscountPolicy(), WeekdayDiscountPolicy(), WeekendDiscountPolicy()) + + diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt new file mode 100644 index 0000000..ba3fe09 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt @@ -0,0 +1,20 @@ +package christmas.discount_policy + +import christmas.dto.OrderMenuDto +import christmas.global.Component + +@Component +class SpecialDiscountPolicy: EventDiscountPolicy { + + override val discountPolicyName: String + get() = "특별 할인" + + override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean { + val days = listOf(3, 10, 17, 24, 25, 31) + return orderMenuDto.reservationDate in days + } + + override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = 1000L + + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt new file mode 100644 index 0000000..8b291ba --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt @@ -0,0 +1,23 @@ +package christmas.discount_policy + +import christmas.domain.MenuType +import christmas.dto.OrderMenuDto +import christmas.global.Component + +@Component +class WeekdayDiscountPolicy: EventDiscountPolicy { + + override val discountPolicyName: String + get() = "평일 할인" + + override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean { + val days = listOf(3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31) + return orderMenuDto.reservationDate in days + } + + override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = + 2023L * orderMenuDto.orderList.entries + .filter { it.key.menuType == MenuType.DESSERT } + .sumOf { it.value } + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt new file mode 100644 index 0000000..5c2b998 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt @@ -0,0 +1,24 @@ +package christmas.discount_policy + +import christmas.domain.MenuType +import christmas.dto.OrderMenuDto +import christmas.global.Component + +@Component +class WeekendDiscountPolicy: EventDiscountPolicy { + + override val discountPolicyName: String + get() = "주말 할인" + + override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean { + val days = listOf(1, 2, 8, 9, 15, 16, 22, 23, 29, 30) + return orderMenuDto.reservationDate in days + } + + override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = + 2023L * orderMenuDto.orderList.entries + .filter { it.key.menuType == MenuType.MAIN } + .sumOf { it.value } + + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/domain/Badge.kt b/kotlin-christmas/src/main/kotlin/christmas/domain/Badge.kt new file mode 100644 index 0000000..27b9dc9 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/domain/Badge.kt @@ -0,0 +1,13 @@ +package christmas.domain + +enum class Badge( + val badgeName: String, + val badgePrice: Long +) { + + SANTA("산타", 20_000L), + TREE("트리", 10_000L), + STAR("별", 5_000L), + NOTHING("없음", 0L), + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/domain/Menu.kt b/kotlin-christmas/src/main/kotlin/christmas/domain/Menu.kt new file mode 100644 index 0000000..d834893 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/domain/Menu.kt @@ -0,0 +1,25 @@ +package christmas.domain + +enum class Menu( + val mainMenuName: String, + val mainMenuPrice: Long, + val menuType: MenuType +) { + MUSHROOM_SOUP("양송이수프", 6_000, MenuType.APPETIZER), + TAPAS("타파스", 5_500, MenuType.APPETIZER), + CAESAR_SALAD("시저샐러드", 8_000, MenuType.APPETIZER), + + T_BONE_STEAK("티본스테이크", 55_000, MenuType.MAIN), + BARBECUE_RIB("바비큐립", 54_000, MenuType.MAIN), + SEAFOOD_PASTA("해산물파스타", 35_000, MenuType.MAIN), + CHRISTMAS_PASTA("크리스마스파스타", 25_000, MenuType.MAIN), + + CHOCOLATE_CAKE("초코케이크", 15_000, MenuType.DESSERT), + ICECREAM("아이스크림", 5_000, MenuType.DESSERT), + + ZERO_COLA("제로콜라", 3_000, MenuType.DRINK), + RED_WINE("레드와인", 60_000, MenuType.DRINK), + CHAMPAGNE("샴페인", 25_000, MenuType.DRINK), + + NOTHING("없음", 0L, MenuType.NOTHING), +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/domain/MenuType.kt b/kotlin-christmas/src/main/kotlin/christmas/domain/MenuType.kt new file mode 100644 index 0000000..cd5c2bb --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/domain/MenuType.kt @@ -0,0 +1,9 @@ +package christmas.domain + +enum class MenuType { + APPETIZER, + MAIN, + DESSERT, + DRINK, + NOTHING, +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/dto/OrderMenuDto.kt b/kotlin-christmas/src/main/kotlin/christmas/dto/OrderMenuDto.kt new file mode 100644 index 0000000..eb93b75 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/dto/OrderMenuDto.kt @@ -0,0 +1,41 @@ +package christmas.dto + +import christmas.discount_policy.EventDiscountPolicy +import christmas.discount_policy.createEventDiscountPolicy +import christmas.domain.Badge +import christmas.domain.Menu + + +class OrderMenuDto( + val reservationDate: Int, + val orderList: Map, +) { + private val eventDiscountPolicies: List = createEventDiscountPolicy() + val amountBeforeDiscount: Long by lazy { orderList.entries.sumOf { it.key.mainMenuPrice * it.value} } + val giveMenu: Menu by lazy { if (amountBeforeDiscount > 120_000L) Menu.CHAMPAGNE else Menu.NOTHING } + + val benefitDetails: Map by lazy { + eventDiscountPolicies.filter { policy -> policy.isSatisfiedBy(this) } + .associate { policy -> policy.discountPolicyName to policy.calculateDiscountAmount(this) } + } + + val totalDiscountAmount: Long by lazy { + if (amountBeforeDiscount > 10_000L) { + return@lazy giveMenu.mainMenuPrice + benefitDetails.entries.sumOf { it.value } + } else { + return@lazy 0 + } + } + + val badge: Badge by lazy { + Badge.entries.first { totalDiscountAmount >= it.badgePrice } + } + + override fun toString(): String { + return buildString { + orderList.entries.forEach { + append(it.key.mainMenuName + " " + it.value + "개\n") + } + } + } +} diff --git a/kotlin-christmas/src/main/kotlin/christmas/global/Component.kt b/kotlin-christmas/src/main/kotlin/christmas/global/Component.kt new file mode 100644 index 0000000..54050a8 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/global/Component.kt @@ -0,0 +1,3 @@ +package christmas.global + +annotation class Component diff --git a/kotlin-christmas/src/main/kotlin/christmas/global/Container.kt b/kotlin-christmas/src/main/kotlin/christmas/global/Container.kt new file mode 100644 index 0000000..95cecbd --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/global/Container.kt @@ -0,0 +1,62 @@ +package christmas.global + +import org.reflections.Reflections +import kotlin.reflect.KClass +import kotlin.reflect.KFunction +import kotlin.reflect.KParameter +import kotlin.reflect.cast + + +object Container { + + private val registeredClasses = mutableSetOf>() + private val cachedInstances = mutableMapOf, Any>() + + fun register(clazz: KClass<*>) { + registeredClasses.add(clazz) + } + + fun getInstance(type: KClass): T { + if (type in cachedInstances) { + return type.cast(cachedInstances[type]) + } + + val instance = registeredClasses.firstOrNull { clazz -> clazz == type } + ?.let { clazz -> instantiate(clazz) as T } + ?: throw IllegalArgumentException("해당 인스턴스 타입을 찾을 수 없습니다.") + + cachedInstances[type] = instance + return instance + } + + private fun instantiate(clazz: KClass): T { + val constructor = findUsableConstructor(clazz) + val params = constructor.parameters + .map { param -> getInstance(param.type.classifier as KClass<*>) } + .toTypedArray() + + return constructor.call(*params) + } + + private fun findUsableConstructor(clazz: KClass): KFunction = + clazz.constructors.firstOrNull { constructor -> + constructor.parameters.isAllRegistered + }?: throw IllegalArgumentException("사용할 수 있는 생성자가 없습니다") + + private val List.isAllRegistered: Boolean + get() = this.all { it.type.classifier in registeredClasses } + + fun componentScan(clazz: KClass<*>) { + val reflections = Reflections(clazz.packageName) + val jClasses = reflections.getTypesAnnotatedWith(Component::class.java) + jClasses.forEach { jClasses -> Container.register(jClasses.kotlin) } + } + + private val KClass<*>.packageName: String + get() { + val qualifiedName = this.qualifiedName ?: throw IllegalArgumentException("익명 객체입니다!") + val hierarchy = qualifiedName.split(".") + return hierarchy.subList(0, hierarchy.lastIndex).joinToString(".") + } + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt b/kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt new file mode 100644 index 0000000..96c290b --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt @@ -0,0 +1,58 @@ +package christmas.view + +import camp.nextstep.edu.missionutils.Console +import christmas.domain.Menu +import christmas.domain.MenuType +import christmas.dto.OrderMenuDto +import christmas.global.Component + +@Component +class InputView { + + companion object { + const val ERROR_PREFIX = "[ERROR]" + } + + fun getVisitDay(): Int { + val visitDay = (Console.readLine() + ?.toIntOrNull() + ?: throw IllegalArgumentException("숫자만 입력해 주세요.")) + + requireNotNull(visitDay in 1..31) { + IllegalArgumentException("$ERROR_PREFIX 유효하지 않은 날짜입니다. 다시 입력해 주세요.") + } + + return visitDay + } + + fun getOrderMenuAndCount(userVisitDay: Int): OrderMenuDto { + val orders = mutableMapOf() + val menuName = Menu.entries.associateBy { it.mainMenuName } + + val menuAndCount = Console.readLine() + ?.split(",") + ?: throw IllegalArgumentException("입력 값이 없습니다.") + + menuAndCount.forEach { item -> + val parts = item.split("-") + val menu = menuName[parts[0]] ?: throw IllegalArgumentException("$ERROR_PREFIX 유효하지 않은 주문입니다. 다시 입력해 주세요.") + val count = parts[1].toInt() + + require(count > 0) { + "$ERROR_PREFIX 유효하지 않은 주문입니다. 다시 입력해 주세요." + } + + orders[menu] = count + } + + if (orders.keys.all { it.menuType == MenuType.DRINK }) throw IllegalArgumentException("$ERROR_PREFIX 음료만 주문 시, 주문할 수 없습니다.") + + + require(orders.values.count() < 20) { + "$ERROR_PREFIX 20개 이상의 음식은 주문하실 수 없습니다." + } + + return OrderMenuDto(userVisitDay, orders) + } + +} \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt b/kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt new file mode 100644 index 0000000..0d09b09 --- /dev/null +++ b/kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt @@ -0,0 +1,58 @@ +package christmas.view + +import christmas.domain.Menu +import christmas.dto.OrderMenuDto +import christmas.global.Component + +@Component +class OutputView { + + fun printEventMessage() { + println(""" + 안녕하세요! 우테코 식당 12월 이벤트 플래너입니다. + 12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!) + """.trimIndent()) + } + + fun printMenuAndCountMessage() { + println("주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2, 레드와인-1, 초코케이크-1)") + } + + fun printEventListMessage(userVisitDay: Int) { + println("12월 ${userVisitDay}에 우테코 식당에서 받을 이벤트 혜택 미리 보기!\n") + } + + fun printReceipt(ordersDto: OrderMenuDto) { + val giveawayMenu = if (ordersDto.giveMenu != Menu.NOTHING) { + ordersDto.giveMenu.mainMenuName + " 1개" + } else { + "없음" + } + + println("<주문 메뉴>") + ordersDto.orderList.forEach { (menu, count) -> + println("${menu.mainMenuName}: $count 개") + } + + println("\n<할인 전 총주문 금액>") + println(String.format("%,d원", ordersDto.amountBeforeDiscount)) + + println("\n<증정 메뉴>") + println(giveawayMenu) + + println("\n<혜택 내역>") + ordersDto.benefitDetails.forEach { (key, value) -> + println("$key: ${String.format("-%,d원", value)}") + } + + println("\n<총혜택 금액>") + println(String.format("-%,d원", ordersDto.totalDiscountAmount)) + + println("\n<할인 후 예상 결제 금액>") + println(String.format("%,d원", ordersDto.amountBeforeDiscount - ordersDto.totalDiscountAmount + ordersDto.giveMenu.mainMenuPrice)) + + println("\n<12월 이벤트 배지>") + println(ordersDto.badge.badgeName) + } + +} \ No newline at end of file diff --git a/kotlin-lotto/src/main/kotlin/lotto/dsl/dockerCompose.kt b/kotlin-lotto/src/main/kotlin/lotto/dsl/dockerCompose.kt new file mode 100644 index 0000000..2aae5e6 --- /dev/null +++ b/kotlin-lotto/src/main/kotlin/lotto/dsl/dockerCompose.kt @@ -0,0 +1,2 @@ +package lotto.dsl + From bf4d8b12f976746eb572d137c6bb153f03930610 Mon Sep 17 00:00:00 2001 From: leesoobeen <02ggang9@gmail.com> Date: Mon, 4 Mar 2024 19:47:41 +0900 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20OrderMenuDto=EB=A5=BC=20domain=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../discount_policy/ChristmasDdayDiscountPolicy.kt | 10 +++++----- .../christmas/discount_policy/EventDiscountPolicy.kt | 6 +++--- .../christmas/discount_policy/SpecialDiscountPolicy.kt | 8 ++++---- .../christmas/discount_policy/WeekdayDiscountPolicy.kt | 10 +++++----- .../christmas/discount_policy/WeekendDiscountPolicy.kt | 10 +++++----- .../{dto/OrderMenuDto.kt => domain/OrderMenu.kt} | 6 ++---- .../src/main/kotlin/christmas/view/InputView.kt | 6 +++--- .../src/main/kotlin/christmas/view/OutputView.kt | 4 ++-- 8 files changed, 29 insertions(+), 31 deletions(-) rename kotlin-christmas/src/main/kotlin/christmas/{dto/OrderMenuDto.kt => domain/OrderMenu.kt} (92%) diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt index 3d743f1..79925df 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/ChristmasDdayDiscountPolicy.kt @@ -1,6 +1,6 @@ package christmas.discount_policy -import christmas.dto.OrderMenuDto +import christmas.domain.OrderMenu import christmas.global.Component @Component @@ -9,9 +9,9 @@ class ChristmasDdayDiscountPolicy: EventDiscountPolicy { override val discountPolicyName: String get() = "크리스마스 디데이 할인" - override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean = - orderMenuDto.reservationDate in 1..25 + override fun isSatisfiedBy(orderMenu: OrderMenu): Boolean = + orderMenu.reservationDate in 1..25 - override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = - 1000L + 100L * (orderMenuDto.reservationDate - 1) + override fun calculateDiscountAmount(orderMenu: OrderMenu): Long = + 1000L + 100L * (orderMenu.reservationDate - 1) } \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt index f277089..9cfcbd2 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/EventDiscountPolicy.kt @@ -1,13 +1,13 @@ package christmas.discount_policy -import christmas.dto.OrderMenuDto +import christmas.domain.OrderMenu interface EventDiscountPolicy { val discountPolicyName: String - fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean + fun isSatisfiedBy(orderMenu: OrderMenu): Boolean - fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long + fun calculateDiscountAmount(orderMenu: OrderMenu): Long } \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt index ba3fe09..7794aa7 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/SpecialDiscountPolicy.kt @@ -1,6 +1,6 @@ package christmas.discount_policy -import christmas.dto.OrderMenuDto +import christmas.domain.OrderMenu import christmas.global.Component @Component @@ -9,12 +9,12 @@ class SpecialDiscountPolicy: EventDiscountPolicy { override val discountPolicyName: String get() = "특별 할인" - override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean { + override fun isSatisfiedBy(orderMenu: OrderMenu): Boolean { val days = listOf(3, 10, 17, 24, 25, 31) - return orderMenuDto.reservationDate in days + return orderMenu.reservationDate in days } - override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = 1000L + override fun calculateDiscountAmount(orderMenu: OrderMenu): Long = 1000L } \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt index 8b291ba..b1dc966 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekdayDiscountPolicy.kt @@ -1,7 +1,7 @@ package christmas.discount_policy import christmas.domain.MenuType -import christmas.dto.OrderMenuDto +import christmas.domain.OrderMenu import christmas.global.Component @Component @@ -10,13 +10,13 @@ class WeekdayDiscountPolicy: EventDiscountPolicy { override val discountPolicyName: String get() = "평일 할인" - override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean { + override fun isSatisfiedBy(orderMenu: OrderMenu): Boolean { val days = listOf(3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 31) - return orderMenuDto.reservationDate in days + return orderMenu.reservationDate in days } - override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = - 2023L * orderMenuDto.orderList.entries + override fun calculateDiscountAmount(orderMenu: OrderMenu): Long = + 2023L * orderMenu.orderList.entries .filter { it.key.menuType == MenuType.DESSERT } .sumOf { it.value } diff --git a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt index 5c2b998..ea0c64b 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/discount_policy/WeekendDiscountPolicy.kt @@ -1,7 +1,7 @@ package christmas.discount_policy import christmas.domain.MenuType -import christmas.dto.OrderMenuDto +import christmas.domain.OrderMenu import christmas.global.Component @Component @@ -10,13 +10,13 @@ class WeekendDiscountPolicy: EventDiscountPolicy { override val discountPolicyName: String get() = "주말 할인" - override fun isSatisfiedBy(orderMenuDto: OrderMenuDto): Boolean { + override fun isSatisfiedBy(orderMenu: OrderMenu): Boolean { val days = listOf(1, 2, 8, 9, 15, 16, 22, 23, 29, 30) - return orderMenuDto.reservationDate in days + return orderMenu.reservationDate in days } - override fun calculateDiscountAmount(orderMenuDto: OrderMenuDto): Long = - 2023L * orderMenuDto.orderList.entries + override fun calculateDiscountAmount(orderMenu: OrderMenu): Long = + 2023L * orderMenu.orderList.entries .filter { it.key.menuType == MenuType.MAIN } .sumOf { it.value } diff --git a/kotlin-christmas/src/main/kotlin/christmas/dto/OrderMenuDto.kt b/kotlin-christmas/src/main/kotlin/christmas/domain/OrderMenu.kt similarity index 92% rename from kotlin-christmas/src/main/kotlin/christmas/dto/OrderMenuDto.kt rename to kotlin-christmas/src/main/kotlin/christmas/domain/OrderMenu.kt index eb93b75..2cd42ff 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/dto/OrderMenuDto.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/domain/OrderMenu.kt @@ -1,12 +1,10 @@ -package christmas.dto +package christmas.domain import christmas.discount_policy.EventDiscountPolicy import christmas.discount_policy.createEventDiscountPolicy -import christmas.domain.Badge -import christmas.domain.Menu -class OrderMenuDto( +class OrderMenu( val reservationDate: Int, val orderList: Map, ) { diff --git a/kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt b/kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt index 96c290b..c8e8d0f 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/view/InputView.kt @@ -3,7 +3,7 @@ package christmas.view import camp.nextstep.edu.missionutils.Console import christmas.domain.Menu import christmas.domain.MenuType -import christmas.dto.OrderMenuDto +import christmas.domain.OrderMenu import christmas.global.Component @Component @@ -25,7 +25,7 @@ class InputView { return visitDay } - fun getOrderMenuAndCount(userVisitDay: Int): OrderMenuDto { + fun getOrderMenuAndCount(userVisitDay: Int): OrderMenu { val orders = mutableMapOf() val menuName = Menu.entries.associateBy { it.mainMenuName } @@ -52,7 +52,7 @@ class InputView { "$ERROR_PREFIX 20개 이상의 음식은 주문하실 수 없습니다." } - return OrderMenuDto(userVisitDay, orders) + return OrderMenu(userVisitDay, orders) } } \ No newline at end of file diff --git a/kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt b/kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt index 0d09b09..75643a1 100644 --- a/kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt +++ b/kotlin-christmas/src/main/kotlin/christmas/view/OutputView.kt @@ -1,7 +1,7 @@ package christmas.view import christmas.domain.Menu -import christmas.dto.OrderMenuDto +import christmas.domain.OrderMenu import christmas.global.Component @Component @@ -22,7 +22,7 @@ class OutputView { println("12월 ${userVisitDay}에 우테코 식당에서 받을 이벤트 혜택 미리 보기!\n") } - fun printReceipt(ordersDto: OrderMenuDto) { + fun printReceipt(ordersDto: OrderMenu) { val giveawayMenu = if (ordersDto.giveMenu != Menu.NOTHING) { ordersDto.giveMenu.mainMenuName + " 1개" } else {