Skip to content

[Step2] 로또(자동) #1099

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

Merged
merged 25 commits into from
Dec 1, 2024
Merged

[Step2] 로또(자동) #1099

merged 25 commits into from
Dec 1, 2024

Conversation

devtaebong
Copy link

안녕하세요! 부족하지만 리뷰 잘 부탁 드립니다🙇‍♂️

Copy link

@Flamme1004K Flamme1004K left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요!

2단계 미션 매우 잘 해주셨습니다. 코멘트 남겼으니 반영 후 다시 리뷰 요청 부탁드립니다.

감사합니다.

Comment on lines 14 to 15
val numberOfLotto = Cashier.purchaseLotto(amount)
val userLottos = List(numberOfLotto) { Lotto.createLotto(randomNumberListGenerator.generate()) }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cashier를 추가하셨군요~ 그렇다면, cashier가 lotto까지 주면 어떨까요?

Comment on lines 9 to 10
private const val MINIMUM_NUMBER = 1
private const val MAXIMUM_NUMBER = 45

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1과 45는 로또에서 매우 의미있는 값이라고 생각해요. 각 클래스마다 선언하지 말고, 한 곳에서 관리하면 어떨까요?

private val NUMBERS: Map<Int, LottoNumber> = (MINIMUM_NUMBER..MAXIMUM_NUMBER).associateWith { LottoNumber(it) }

fun from(number: Int): LottoNumber {
return NUMBERS[number] ?: throw IllegalArgumentException()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외에 대한 메시지는 있으면 좋겠죠?

winningLotto: Lotto,
): List<Statistics> {
val groupByRanking: Map<Int, List<Lotto>> =
(5 downTo 1).associateWith { emptyList<Lotto>() } +

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(5 downTo 1) 은 무엇을 의미하는 걸까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

의미있는 상수로 분리했습니다 🙇‍♂️

println(WINNING_NUMBER_INPUT_MESSAGE)
return readln().replace(REPLACEMENT_SOURCE, REPLACEMENT_TARGET)
.split(WINNING_NUMBER_DELIMITERS)
.also { if (it.size != 6) throw IllegalArgumentException(ERROR_WRONG_NUMBER_COUNT) }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if문 대신에 require()를 사용해 볼까요?

println(LOTTO_STATISTICS_SEPARATOR)
statisticsList.filter { it.rank in 1..5 && it.rank != 2 }
.forEach { printLottoResult(it) }
val earningRatio = Statistics.calculateEarningRatio(statisticsList, amount)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인에 대한 로직이 뷰에 있네요. 분리해 볼까요?

Copy link

@Flamme1004K Flamme1004K left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요!

2단계 미션 피드백 매우 잘 반영해 주셨어요! 이만 머지하도록 하겠습니다.

3단계 미션 화이팅입니다!

Comment on lines 9 to 16
fun purchaseLotto(
amount: Int,
lottoNumberListGenerator: LottoNumberListGenerator,
): List<Lotto> {
require(amount >= LOTTO_PRICE)
return amount / LOTTO_PRICE
val numberOfLotto = calculateNumberOfLotto(amount)
return List(numberOfLotto) { Lotto.createLotto(lottoNumberListGenerator.generate()) }
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

피드백 반영 잘 해주셨어요! 👍

Comment on lines +3 to +14
const val MINIMUM_NUMBER = 1
const val MAXIMUM_NUMBER = 45
const val FIRST_RANK = 1
const val SECOND_RANK = 2
const val THIRD_RANK = 3
const val FOURTH_RANK = 4
const val FIFTH_RANK = 5
const val NO_RANK = 0
const val MATCH_COUNT_SIX = 6
const val MATCH_COUNT_FIVE = 5
const val MATCH_COUNT_FOUR = 4
const val MATCH_COUNT_THREE = 3

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3단계 미션 때 많은 변화 기대하겠습니다 😊

Comment on lines +3 to +17
@JvmInline
value class LottoNumber(val number: Int) {
companion object {
private const val MINIMUM_NUMBER = 1
private const val MAXIMUM_NUMBER = 45
private const val CANNOT_CREATE_LOTTO_NUMBER_MESSAGE = "Lotto number must be between %s and %s."

private val NUMBERS: Map<Int, LottoNumber> = (MINIMUM_NUMBER..MAXIMUM_NUMBER).associateWith { LottoNumber(it) }

fun from(number: Int): LottoNumber {
return NUMBERS[number] ?: throw IllegalArgumentException(
CANNOT_CREATE_LOTTO_NUMBER_MESSAGE.format(MINIMUM_NUMBER, MAXIMUM_NUMBER),
)
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@JvmInline
value class LottoNumber(val number: Int) {
companion object {
private const val MINIMUM_NUMBER = 1
private const val MAXIMUM_NUMBER = 45
private const val CANNOT_CREATE_LOTTO_NUMBER_MESSAGE = "Lotto number must be between %s and %s."
private val NUMBERS: Map<Int, LottoNumber> = (MINIMUM_NUMBER..MAXIMUM_NUMBER).associateWith { LottoNumber(it) }
fun from(number: Int): LottoNumber {
return NUMBERS[number] ?: throw IllegalArgumentException(
CANNOT_CREATE_LOTTO_NUMBER_MESSAGE.format(MINIMUM_NUMBER, MAXIMUM_NUMBER),
)
}
}
@JvmInline
value class LottoNumber(val number: Int) {
init {
require(NUMBERS[number] != null) {
CANNOT_CREATE_LOTTO_NUMBER_MESSAGE.format(MINIMUM_NUMBER, MAXIMUM_NUMBER)
}
}
companion object {
private const val MINIMUM_NUMBER = 1
private const val MAXIMUM_NUMBER = 45
private const val CANNOT_CREATE_LOTTO_NUMBER_MESSAGE = "Lotto number must be between %s and %s."
private val NUMBERS: Map<Int, LottoNumber> = (MINIMUM_NUMBER..MAXIMUM_NUMBER).associateWith { LottoNumber(it) }
}
...

저는 코틀린을 사용하면서 정적 팩토리 메서드는 좀 더 명확하게 쓰이게 되더라구요. 기본 프로퍼티인 경우에는 기본 생성자를 쓰게 되고, 그 외 기본 프로퍼티와 다른 타입의 프로퍼티가 있으면 정적 팩토리 메서드를 사용하는 게 더 어울린다고 생각합니다. 그럼으로써 기본 프로퍼티를 적극적으로 사용하면, 초기 검증 로직도 init에 넣을 수 있어 더 직관적으로 보이는 부분도 있었어요.

이처럼 기본 생성자를 적극적으로 사용하고, init을 적극적으로 사용해보는 건 어떨까요?

Comment on lines +19 to +21
override fun toString(): String {
return number.toString()
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toString은 제거해도 좋아보이네요~

import lotto.constant.NO_RANK
import lotto.constant.THIRD_RANK

class Match {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모든 함수가 정적 함수로 되어있네요~ object class를 사용해 보는 건 어떨까요?

import lotto.constant.MINIMUM_NUMBER

class RandomLottoNumberListGenerator : LottoNumberListGenerator {
override fun generate(): List<Int> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
override fun generate(): List<Int> {
override fun generate(): List<LottoNumber> {

Int가 아닌, LottoNumber를 주면 어떨까요?

@@ -0,0 +1,11 @@
package lotto.domain

class Lotto private constructor(val lottoNumbers: Set<LottoNumber>) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도, init 메서드를 통하여 lottoNumbers가 6개로 이루어졌는지 검증 로직을 추가하면 어떨까요?

sut.lottoNumbers.size shouldBe 6
}

it("로또의 각 숫자는 오름차순으로 정렬 되어 있어야 한다.") {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

임의의 숫자가 아닌, 직접 숫자(Lotto.createLotto(List.of(1,2,3,4,5,6)))를 넣어서 검증하는 테스트도 있으면 좋겠네요~

Comment on lines +5 to +9
class FifthRankLottoNumberGenerator : LottoNumberListGenerator {
override fun generate(): List<Int> {
return listOf(1, 2, 3, 43, 44, 45)
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코틀린 파일에는 여러개의 클래스를 넣을 수 있어요. Fake 객체에 대해서는 하나의 코틀린 파일에 넣고 관리하는 게 어떨까요?

@@ -0,0 +1,25 @@
package lotto.domain

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트에 대해서 대부분 성공에 대한 테스트로 이루어져 있네요.

예외에 대한 테스트 케이스도 있었으면 좋겠어요. 예외에 대해서 빠르게 체크하기 위해서도 테스트 케이스를 만드는 거라고 생각합니다.
항상 해피 케이스만 있는 건 아니잖아요!? 😊

@Flamme1004K Flamme1004K merged commit 48c5fef into next-step:devtaebong Dec 1, 2024
@Flamme1004K
Copy link

#1099 (comment)

에 대한 코멘트 확인 부탁드려요!

그리고, 커밋 로그에 대해서 fix: 피드백 반영 가 아닌, 좀 더 기능적으로 어떻게 반영했는지 나누어서 커밋 로그를 만들었으면 좋겠어요!

ex)

  • apply: 로또 번호 상수 이동
  • apply: cashier를 통해 로또 구매 시 구매 한 로또를 반환하도록 변경

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants