-
Notifications
You must be signed in to change notification settings - Fork 362
Step1 풀리퀘 올립니다~! #1164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ssonsh
wants to merge
8
commits into
next-step:ssonsh
Choose a base branch
from
ssonsh:step1
base: ssonsh
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Step1 풀리퀘 올립니다~! #1164
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
cf2a069
commit test
ssonzk 484a35a
docs: 구현 요구사항 README 정리
bekker fe4fd2c
feat: Domain 및 Service 로직 뼈대 개발
bekker c86a72a
feat: Domain 및 Service 로직 테스트 작성
bekker eb6ab86
feat: Domain 및 Service 로직 작성
bekker 1953c47
feat: View 작성 및 Constant 추가
bekker 657f371
feat: Application 구현 및 버그 수정
bekker d96aade
fix: 테스트 컴파일 실패 수정
bekker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,69 @@ | ||
| # kotlin-lotto | ||
| # kotlin-lotto | ||
|
|
||
| ## 기능 목록 | ||
| ### 메인 애플리케이션 진입점 | ||
| - MyLottoApplication.kt | ||
| - view 와 비즈니스 로직을 연결 | ||
| - 종료 안시키고 무한루프로 입력 | ||
| - 종료는 ctrl c로 | ||
|
|
||
| ### View | ||
| #### InputView | ||
| ##### 구입 금액 입력 | ||
| - `구입금액을 입력해 주세요.` | ||
| - 입력된 금액을 반환 | ||
| - 1000원 단위 | ||
| - 0원 초과 | ||
| - 검증 실패 -> `다시 입력하세요` | ||
|
|
||
| ##### 당첨 번호 입력 | ||
| - `지난 주 당첨 번호를 입력해 주세요.` | ||
| - 입력된 6개 당첨번호 set을 받아서 LottoNumbers를 반환 | ||
| - 콤마로 구분 | ||
| - 검증 실패 -> `다시 입력하세요` | ||
|
|
||
| #### ResultView | ||
| ##### 구매한 매수 및 번호 출력 | ||
| - LottoTicket 일급 컬렉션 리스트를 입력으로 받음 | ||
| - `14개를 구매했습니다.` | ||
| - `[8, 21, 23, 41, 42, 43]` | ||
|
|
||
| ##### 당첨 통계 출력 | ||
| - LottoResult 를 입력으로 받음 | ||
| - 일치 개수 별로 당첨된 개수 및 수익률 및 훈수 메세지 | ||
|
|
||
| ### Domain | ||
| #### LottoNumber | ||
| - 숫자 하나. | ||
| - 1~45 사이 숫자 | ||
|
|
||
| #### LottoWinningNumbers | ||
| - 6개 짜리 LottoNumber Set이 있음. (나중에 보너스볼 추가될 예정) | ||
| - 검증 메소드 | ||
| - 중복 불가 | ||
|
|
||
| #### LottoTicket | ||
| - 6개 짜리 LottoNumber Set이 있음. | ||
|
|
||
| #### Rank enum | ||
| - 등수별로 당첨 금액 | ||
|
|
||
| #### LottoResult | ||
| - 티켓 n개에 대해서 결과를 가지고 있음. + 수익률 계산 | ||
|
|
||
| #### LottoService | ||
| - generateLottoTickets | ||
| - 구입 금액 입력 받아서 LottoTicket 리스트 반환하는 method | ||
| - shuffled sorted 사용 | ||
| - matchLottoTicket | ||
| - LottoTicket + 당첨 번호 입력 받아서 당첨된 Rank를 반환하는 method | ||
| - matchLottoTickets | ||
| - LottoTicket 리스트 + 당첨 번호 입력 받아서 LottoResult 반환하는 method | ||
| - 위의 단건 매칭 method를 내부에서 사용 | ||
|
|
||
| ## 기능 구현 단위 | ||
| - domain 객체 뼈대 구현 + service 뼈대 구현 | ||
| - 뼈대에 대한 테스트 작성 | ||
| - domain 객체 및 service 로직 작성 | ||
| - view 로직 구현 | ||
| - application 구현 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package com.example.mylotto | ||
|
|
||
| import com.example.mylotto.model.LottoNumber | ||
| import com.example.mylotto.model.LottoResult | ||
| import com.example.mylotto.model.LottoTicket | ||
| import com.example.mylotto.model.LottoWinningNumbers | ||
| import com.example.mylotto.service.LottoService | ||
| import com.example.mylotto.view.InputView | ||
| import com.example.mylotto.view.ResultView | ||
|
|
||
| fun main() { | ||
| while (true) { | ||
| doLotto() | ||
| } | ||
| } | ||
|
|
||
| fun doLotto() { | ||
| val lottoService = LottoService() | ||
| val inputView = InputView() | ||
| val resultView = ResultView() | ||
|
|
||
| var lottoTickets: List<LottoTicket> | ||
|
|
||
| while (true) { | ||
| try { | ||
| val purchaseAmount = inputView.readPurchaseAmount() | ||
| lottoTickets = lottoService.generateLottoTickets(purchaseAmount) | ||
| resultView.displayPurchasedTickets(lottoTickets) | ||
| break | ||
| } catch (e: Exception) { | ||
| println("다시 입력해주세요") | ||
| } | ||
| } | ||
|
|
||
| while (true) { | ||
| try { | ||
| val winningNumbers: LottoWinningNumbers = LottoWinningNumbers.of(inputView.readWinningNumbers().map(::LottoNumber)) | ||
| val result = LottoResult.of(lottoTickets.map { ticket -> lottoService.matchLottoTicket(ticket, winningNumbers) }) | ||
| resultView.displayWinningStatistics(result) | ||
| break | ||
| } catch (e: Exception) { | ||
| println("다시 입력해주세요") | ||
| } | ||
| } | ||
|
|
||
| println() | ||
| } |
9 changes: 9 additions & 0 deletions
9
src/main/kotlin/com/example/mylotto/constant/LottoConstant.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.example.mylotto.constant | ||
|
|
||
| class LottoConstant { | ||
| companion object { | ||
| const val LOTTO_NUMBER_MAX = 45 | ||
| const val LOTTO_NUMBER_SIZE = 6 | ||
| const val LOTTO_TICKET_PRICE = 1000 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package com.example.mylotto.enum | ||
|
|
||
| enum class Rank( | ||
| val countOfMatch: Int, | ||
| val winningMoney: Int, | ||
| ) { | ||
| FIRST(6, 2_000_000_000), | ||
| THIRD(5, 1_500_000), | ||
| FOURTH(4, 50_000), | ||
| FIFTH(3, 5_000), | ||
| MISS(0, 0), | ||
| ; | ||
|
|
||
| companion object { | ||
| fun valueOf(countOfMatch: Int): Rank = | ||
| when (countOfMatch) { | ||
| 6 -> FIRST | ||
| 5 -> THIRD | ||
| 4 -> FOURTH | ||
| 3 -> FIFTH | ||
| else -> MISS | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.example.mylotto.model | ||
|
|
||
| import com.example.mylotto.constant.LottoConstant | ||
|
|
||
| data class LottoNumber( | ||
| val number: Int, | ||
| ) { | ||
| init { | ||
| require(number in 1..LottoConstant.LOTTO_NUMBER_MAX) { "Number must be between 1 and 45." } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.example.mylotto.model | ||
|
|
||
| import com.example.mylotto.constant.LottoConstant | ||
| import com.example.mylotto.enum.Rank | ||
|
|
||
| class LottoResult private constructor( | ||
| val rankCountMap: Map<Rank, Int>, | ||
| val profitRate: Double, | ||
| ) { | ||
| companion object { | ||
| fun of(ranks: List<Rank>): LottoResult { | ||
| val rankCountMap = ranks.groupingBy { it }.eachCount() | ||
| val totalWinnings = rankCountMap.entries.sumOf { it.key.winningMoney * it.value } | ||
| val profitRate = totalWinnings.toDouble() / ranks.size / LottoConstant.LOTTO_TICKET_PRICE | ||
| return LottoResult( | ||
| rankCountMap = rankCountMap, | ||
| profitRate = profitRate, | ||
| ) | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.example.mylotto.model | ||
|
|
||
| import com.example.mylotto.constant.LottoConstant | ||
|
|
||
| class LottoTicket( | ||
| val numbers: Set<LottoNumber>, | ||
| ) { | ||
| constructor() : this( | ||
| (1..LottoConstant.LOTTO_NUMBER_MAX) | ||
| .shuffled() | ||
| .take(LottoConstant.LOTTO_NUMBER_SIZE) | ||
| .sorted() | ||
| .map { LottoNumber(it) } | ||
| .toSet(), | ||
| ) | ||
| } |
16 changes: 16 additions & 0 deletions
16
src/main/kotlin/com/example/mylotto/model/LottoWinningNumbers.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.example.mylotto.model | ||
|
|
||
| import com.example.mylotto.constant.LottoConstant | ||
|
|
||
| class LottoWinningNumbers private constructor( | ||
| val numbers: Set<LottoNumber>, | ||
| ) { | ||
| companion object { | ||
| fun of(numberList: List<LottoNumber>): LottoWinningNumbers { | ||
| require(numberList.size == LottoConstant.LOTTO_NUMBER_SIZE) { "There must be exactly 6 winning numbers." } | ||
| val set = numberList.toSet() | ||
| require(set.size == LottoConstant.LOTTO_NUMBER_SIZE) { "There must be no duplicates." } | ||
| return LottoWinningNumbers(set) | ||
| } | ||
| } | ||
| } |
26 changes: 26 additions & 0 deletions
26
src/main/kotlin/com/example/mylotto/service/LottoService.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package com.example.mylotto.service | ||
|
|
||
| import com.example.mylotto.constant.LottoConstant | ||
| import com.example.mylotto.enum.Rank | ||
| import com.example.mylotto.model.LottoTicket | ||
| import com.example.mylotto.model.LottoWinningNumbers | ||
|
|
||
| class LottoService { | ||
| fun generateLottoTickets(purchaseAmount: Long): List<LottoTicket> { | ||
| require(purchaseAmount > 0 && purchaseAmount % LottoConstant.LOTTO_TICKET_PRICE == 0L) { | ||
| "Purchase amount must be a positive multiple of ${LottoConstant.LOTTO_TICKET_PRICE}." | ||
| } | ||
| val ticketCount = (purchaseAmount / LottoConstant.LOTTO_TICKET_PRICE).toInt() | ||
| return List(ticketCount) { | ||
| LottoTicket() | ||
| } | ||
| } | ||
|
|
||
| fun matchLottoTicket( | ||
| lottoTicket: LottoTicket, | ||
| winningNumbers: LottoWinningNumbers, | ||
| ): Rank { | ||
| val matchedCount = lottoTicket.numbers.intersect(winningNumbers.numbers).size | ||
| return Rank.valueOf(matchedCount) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.example.mylotto.view | ||
|
|
||
| class InputView { | ||
| fun readPurchaseAmount(): Long { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 구입금액을 천원 으로 입력하면 어떻게 되나요? |
||
| println("구입금액을 입력해 주세요.") | ||
| val amount = readlnOrNull()?.toLongOrNull() | ||
| require(amount != null) | ||
| return amount | ||
| } | ||
|
|
||
| fun readWinningNumbers(): List<Int> { | ||
| println("지난 주 당첨 번호를 입력해 주세요.") | ||
| val numbers = | ||
| readlnOrNull() | ||
| ?.split(",") | ||
| ?.mapNotNull { it.trim().toIntOrNull() } | ||
| require(numbers != null) | ||
| return numbers | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package com.example.mylotto.view | ||
|
|
||
| import com.example.mylotto.enum.Rank | ||
| import com.example.mylotto.model.LottoResult | ||
| import com.example.mylotto.model.LottoTicket | ||
|
|
||
| class ResultView { | ||
| fun displayPurchasedTickets(tickets: List<LottoTicket>) { | ||
| println("${tickets.size}개를 구매했습니다.") | ||
| tickets.forEach { ticket -> | ||
| println(ticket.numbers.joinToString(prefix = "[", postfix = "]") { it.number.toString() }) | ||
| } | ||
|
|
||
| println() | ||
| } | ||
|
|
||
| fun displayWinningStatistics(result: LottoResult) { | ||
| println() | ||
| println("당첨 통계") | ||
| println("---------") | ||
|
|
||
| Rank.entries | ||
| .filter { it != Rank.MISS } | ||
| .sortedBy { it.winningMoney } | ||
| .forEach { rank -> | ||
| val count = result.rankCountMap.getOrDefault(rank, 0) | ||
| println("${rank.countOfMatch}개 일치 (${rank.winningMoney}원)- ${count}개") | ||
| } | ||
|
|
||
| print("총 수익률은 ${"%.2f".format(result.profitRate)}입니다.") | ||
|
|
||
| if (result.profitRate < 1.0) { | ||
| println("(기준이 1이기 때문에 결과적으로 손해라는 의미임)") | ||
| } else { | ||
| println("(이득)") | ||
| } | ||
| } | ||
| } |
20 changes: 20 additions & 0 deletions
20
src/test/kotlin/com/example/mylotto/model/LottoNumberTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.example.mylotto.model | ||
|
|
||
| import io.kotest.assertions.throwables.shouldNotThrowAny | ||
| import io.kotest.assertions.throwables.shouldThrow | ||
| import io.kotest.core.spec.style.FunSpec | ||
|
|
||
| class LottoNumberTest : | ||
| FunSpec({ | ||
| test("success") { | ||
| (1..45).forEach { | ||
| shouldNotThrowAny { LottoNumber(it) } | ||
| } | ||
| } | ||
|
|
||
| test("should throw an exception for numbers outside 1 to 45 range") { | ||
| listOf(-1, 0, 46, 100).forEach { | ||
| shouldThrow<IllegalArgumentException> { LottoNumber(it) } | ||
| } | ||
| } | ||
| }) |
24 changes: 24 additions & 0 deletions
24
src/test/kotlin/com/example/mylotto/model/LottoResultTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package com.example.mylotto.model | ||
|
|
||
| import com.example.mylotto.enum.Rank | ||
| import io.kotest.core.spec.style.BehaviorSpec | ||
| import io.kotest.matchers.shouldBe | ||
|
|
||
| class LottoResultTest : | ||
| BehaviorSpec({ | ||
| given("a list of ranks") { | ||
| val ranks = listOf(Rank.FIRST, Rank.THIRD, Rank.THIRD, Rank.THIRD) | ||
|
|
||
| `when`("a LottoResult is created") { | ||
| val lottoResult = LottoResult.of(ranks) | ||
|
|
||
| then("the rank count map should correctly group counts") { | ||
| val rankCounts = lottoResult.rankCountMap | ||
|
|
||
| rankCounts[Rank.FIRST].shouldBe(1) | ||
| rankCounts[Rank.THIRD].shouldBe(3) | ||
| rankCounts[Rank.FOURTH].shouldBe(null) | ||
| } | ||
| } | ||
| } | ||
| }) |
11 changes: 11 additions & 0 deletions
11
src/test/kotlin/com/example/mylotto/model/LottoTicketTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.example.mylotto.model | ||
| import io.kotest.core.spec.style.FunSpec | ||
| import io.kotest.matchers.shouldBe | ||
|
|
||
| class LottoTicketTest : | ||
| FunSpec({ | ||
| test("success") { | ||
| val newTicket = LottoTicket() | ||
| newTicket.numbers.size.shouldBe(6) | ||
| } | ||
| }) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
require 를 쓰셨군요. 나도 써야지..