-
Notifications
You must be signed in to change notification settings - Fork 360
[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
[Step2] 로또(자동) #1099
Conversation
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.
안녕하세요!
2단계 미션 매우 잘 해주셨습니다. 코멘트 남겼으니 반영 후 다시 리뷰 요청 부탁드립니다.
감사합니다.
val numberOfLotto = Cashier.purchaseLotto(amount) | ||
val userLottos = List(numberOfLotto) { Lotto.createLotto(randomNumberListGenerator.generate()) } |
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.
cashier를 추가하셨군요~ 그렇다면, cashier가 lotto까지 주면 어떨까요?
private const val MINIMUM_NUMBER = 1 | ||
private const val MAXIMUM_NUMBER = 45 |
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.
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() |
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.
예외에 대한 메시지는 있으면 좋겠죠?
winningLotto: Lotto, | ||
): List<Statistics> { | ||
val groupByRanking: Map<Int, List<Lotto>> = | ||
(5 downTo 1).associateWith { emptyList<Lotto>() } + |
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.
(5 downTo 1) 은 무엇을 의미하는 걸까요?
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.
의미있는 상수로 분리했습니다 🙇♂️
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) } |
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.
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) |
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.
도메인에 대한 로직이 뷰에 있네요. 분리해 볼까요?
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.
안녕하세요!
2단계 미션 피드백 매우 잘 반영해 주셨어요! 이만 머지하도록 하겠습니다.
3단계 미션 화이팅입니다!
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()) } | ||
} |
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.
피드백 반영 잘 해주셨어요! 👍
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 |
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.
3단계 미션 때 많은 변화 기대하겠습니다 😊
@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), | ||
) | ||
} | ||
} |
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.
@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을 적극적으로 사용해보는 건 어떨까요?
override fun toString(): String { | ||
return number.toString() | ||
} |
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.
toString은 제거해도 좋아보이네요~
import lotto.constant.NO_RANK | ||
import lotto.constant.THIRD_RANK | ||
|
||
class Match { |
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.
모든 함수가 정적 함수로 되어있네요~ object class를 사용해 보는 건 어떨까요?
import lotto.constant.MINIMUM_NUMBER | ||
|
||
class RandomLottoNumberListGenerator : LottoNumberListGenerator { | ||
override fun generate(): List<Int> { |
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.
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>) { |
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.
여기도, init 메서드를 통하여 lottoNumbers가 6개로 이루어졌는지 검증 로직을 추가하면 어떨까요?
sut.lottoNumbers.size shouldBe 6 | ||
} | ||
|
||
it("로또의 각 숫자는 오름차순으로 정렬 되어 있어야 한다.") { |
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.
임의의 숫자가 아닌, 직접 숫자(Lotto.createLotto(List.of(1,2,3,4,5,6)))를 넣어서 검증하는 테스트도 있으면 좋겠네요~
class FifthRankLottoNumberGenerator : LottoNumberListGenerator { | ||
override fun generate(): List<Int> { | ||
return listOf(1, 2, 3, 43, 44, 45) | ||
} | ||
} |
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.
코틀린 파일에는 여러개의 클래스를 넣을 수 있어요. Fake 객체에 대해서는 하나의 코틀린 파일에 넣고 관리하는 게 어떨까요?
@@ -0,0 +1,25 @@ | |||
package lotto.domain |
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.
테스트에 대해서 대부분 성공에 대한 테스트로 이루어져 있네요.
예외에 대한 테스트 케이스도 있었으면 좋겠어요. 예외에 대해서 빠르게 체크하기 위해서도 테스트 케이스를 만드는 거라고 생각합니다.
항상 해피 케이스만 있는 건 아니잖아요!? 😊
에 대한 코멘트 확인 부탁드려요! 그리고, 커밋 로그에 대해서 fix: 피드백 반영 가 아닌, 좀 더 기능적으로 어떻게 반영했는지 나누어서 커밋 로그를 만들었으면 좋겠어요! ex)
|
안녕하세요! 부족하지만 리뷰 잘 부탁 드립니다🙇♂️