Skip to content
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

[아크] 2단계 로또 제출합니다. #31

Merged
merged 35 commits into from
Feb 22, 2023
Merged

Conversation

re4rk
Copy link

@re4rk re4rk commented Feb 20, 2023

입력 부분에 있어서 수정을 많이 하였고 논리적인 오류가 아니면 null을 반환하여 다시 처리하도록 하였습니다!

re4rk added 20 commits February 20, 2023 16:33
Copy link

@malibinYun malibinYun left a comment

Choose a reason for hiding this comment

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

2단계 미션 고생 많으셨어요 :)
피드백을 잘 반영해주셨네요 👍
코드가 이미 깔끔하고 잘 분리되어있어서, 추가 요구사항을 반영하는 데 큰 어려움이 없으셨을 것 같아요 🙂
고민해보면 좋을 점들에 코멘트 남겨두었습니다.
피드백 반영하시고 다시 리뷰 요청 부탁드릴게요!
질문이 있으시다면 언제든지 코멘트나 슬랙 DM으로 질문 주셔도 좋습니다 😊

Comment on lines 30 to 31
private fun getChargeMoney(money: Money, manualLottos: List<Lotto>): Money =
Money.from(money.value - lottoStore.lottoPrice * manualLottos.size)

Choose a reason for hiding this comment

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

남은 돈을 구하기 위해서 수동 티켓 전체가 필요할까요? :)

Copy link
Author

Choose a reason for hiding this comment

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

사이즈를 전달하도록 수정하였습니다! 반영 커밋 : e4c8bf2

Comment on lines 8 to 9

fun buyLotto(amount: Int): List<Lotto> = LottoFactory(lottoGenerator).create(getCount(Amount.from(amount)))
fun buyManualLotto(money: Money, vararg lottos: Lotto): List<Lotto> {

Choose a reason for hiding this comment

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

이미 Lotto가 파라미터로 들어오고 있어 큰 의미를 부여하기 어려워 보입니다.
Lotto로 변환하는 역할을 이 객체에 위임해보는 것은 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

말씀하신대로 InputView에서 객체 생성은 아닌 것같습니다! 반영 커밋 : fd6fe10

Comment on lines 14 to 17
fun buyAutoLotto(money: Money): List<Lotto> {
require(money.value >= LOTTO_PRICE) { ERROR_INVALID_MONEY.format(money.value) }
return LottoFactory(lottoGenerator).createLottos(getCount(money))
}

Choose a reason for hiding this comment

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

LottoFactory와 LottoStore는 하는 역할이 겹쳐보이네요.
또, 자동 티켓을 만들 때마다 LottoFactory 객체를 생성하는 것은 불필요해보입니다 :)

객체 간 역할을 다시 생각해보시고, 어느 한 쪽을 통폐합 하면 좋겠네요 🙂

Copy link
Author

Choose a reason for hiding this comment

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

저도 해당 내용에 대해 고민하고 있었어서 바로 수정하였습니다! 반영 커밋 : 50f2bee

Comment on lines 12 to 14
Rank.values().associateWith { rank ->
lottos.count { rank == Rank.valueOf(getCountOfMatch(it), matchBonus(it)) }
},
lottos.count { rank == Rank.valueOf(it.countMatch(lotto), it.contains(bonusNumber)) }
}.filterValues { it > 0 },

Choose a reason for hiding this comment

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

Rank.values() 로 먼저 값을 채우지 않고 시도해보세요 :)
갯수를 0이 아닌 것을 필터링 하더라도, 결국 불필요한 연산을 하고 있습니다.

먼저, 1장의 티켓을 비교하는 메서드를 분리해보세요 :)

Copy link
Author

Choose a reason for hiding this comment

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

생각해보니 불필요한 연산이 많이 있던 것같았습니다! 그룹화 하여 값을 생성하도록 변경 하였습니다! 반영 커밋 : bc2ef3e

Comment on lines 10 to 22
@Test
fun `수동 로또를 구매한다`() {
val money = Money.from(1000)
val lotto: List<Lotto> = lottoStore.buyManualLotto(money, Lotto(1, 2, 3, 4, 5, 6))
assertThat(lotto.size).isEqualTo(1)
}

@Test
fun `자동 로또를 구매한다`() {
val money = Money.from(10000)
val lotto: List<Lotto> = lottoStore.buyAutoLotto(money)
assertThat(lotto.size).isEqualTo(10)
}

Choose a reason for hiding this comment

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

LottoStore는 Lotto를 생성하는 객체이기에, size 뿐만 아니라 원하는 티켓을 생성했는지도 검증해보는 것은 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

해당 내용을 이해한대로 나올 수 있는 경우가 유효한 경우인지 판단하였는데 이게 맞는 방법인지는 잘 모르겠습니다!
반영 커밋 : 7740c74

Copy link

@malibinYun malibinYun left a comment

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 4
@JvmInline
value class LottoNumber private constructor(val number: Int) {

Choose a reason for hiding this comment

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

value class를 활용해주셨네요 👍

Comment on lines 6 to 7
val lottoPrice: Int
get() = LOTTO_PRICE

Choose a reason for hiding this comment

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

멤버변수에 할당하기 보다, 직접 LOTTO_PRICE를 접근해보는 것은 어떨까요?

Comment on lines 9 to 17
fun buyManualLotto(money: Money, vararg lottos: IntArray): List<Lotto> {
require(money.value >= LOTTO_PRICE * lottos.size) { ERROR_INVALID_COUNT.format(money.value) }
return lottos.map(::Lotto)
}

fun buyAutoLotto(money: Money): List<Lotto> {
require(money.value >= LOTTO_PRICE) { ERROR_INVALID_MONEY.format(money.value) }
return createLottos(getCount(money))
}

Choose a reason for hiding this comment

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

List<Lotto>를 일급 컬렉션으로 만들어보세요 🙂

Copy link
Author

Choose a reason for hiding this comment

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

수동과 자동이 나눠졌기 때문에 일급 컬렉션을 쓰니 훨씬 편해진 것같습니다! 감사합니다! 반영 커밋 : 6bf5fdf

Comment on lines 19 to 22
fun createLottos(count: Int): List<Lotto> {
require(count in MINIMUM_COUNT..MAXIMUM_COUNT) { ERROR_CREATE_COUNT.format(count) }
return List(count) { createLotto() }
}

Choose a reason for hiding this comment

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

이 메서드는 이 객체에서만 쓰이니 private으로 가리거나, 통폐합해보는 것은 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

해당 기능 반영 하였습니다! 반영 커밋 : b415044

Comment on lines 26 to 27
private fun getCount(money: Money): Int = money.value / LOTTO_PRICE

Choose a reason for hiding this comment

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

LottoStore에서는 딱 로또의 발급 기능만 분리해보는 것은 어떨지 제안해봐요 :)
발급 받는 데 반드시 Money가 필요하다는 현실 세계의 반영이지만,
사실 발급기 그 자체에게는 돈이 필요하지 않지요 😊

Copy link
Author

Choose a reason for hiding this comment

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

해당 기능을 Money 객체에게 넘기고 Store에는 Money가 들어가지 않고 Count가 들어가도록 수정하였습니다! 반영 커밋 : b415044

Comment on lines 11 to 16
LottoResult(
Rank.values().associateWith { rank ->
lottos.count { rank == Rank.valueOf(getCountOfMatch(it), matchBonus(it)) }
},
lottos
.map { Rank.valueOf(it.countMatch(lotto), it.contains(bonusNumber)) }
.groupBy { it }
.mapValues { it.value.size },
)

Choose a reason for hiding this comment

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

이 부분 고민 정말 많이 하셨을 것 같은데, 제가 의도하는 대로 구현해주셨네요 👍👍

아래 처럼도 구성해볼 수 있답니다 :) 참고해보세요!

    return lottos.asSequence()
            .map { Rank.valueOf(it.countMatch(lotto), it.contains(bonusNumber)) }
            .groupingBy { it }
            .eachCount()
            .let { LottoResult(it)  }

Copy link
Author

Choose a reason for hiding this comment

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

찾아본 결과 시퀀스는 대용량에 좋다고 이해할 수 있었습니다! 감사합니다! 반영 커밋 : 5b2633e

Comment on lines 16 to 18
assertThat(lottos.size).isEqualTo(2)
assertThat(lottos[0].toList().map { it.number }).isEqualTo(listOf(1, 2, 3, 4, 5, 6))
assertThat(lottos[1].toList().map { it.number }).isEqualTo(listOf(1, 11, 22, 33, 44, 45))

Choose a reason for hiding this comment

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

검증문을 여러 개 쓰는 것과 assertAll로 감싸는 것은 어떤 차이점이 있을까요?

Choose a reason for hiding this comment

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

이 피드백은 조금 어려우셨을까요?
혹은 더 자세한 설명이 필요하신가요?

Comment on lines 26 to 29
lottos.forEach { lotto ->
assertThat(lotto.toList()).hasSize(6)
lotto.toList().forEach { assertThat(it.number).isBetween(1, 45) }
}

Choose a reason for hiding this comment

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

반복문으로 검증문을 실행하는 것은 가독성을 해칩니다.
어떤 시점에 어떤 값을 비교하는 지 어렵기 때문이에요.

사이즈와 숫자의 범위를 검증하는 것은 큰 의미를 갖기 어렵습니다.
실제로 생성한 lotto가 정확하게 예측된 값을 가지는 지 비교해보는 것은 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

예측할 수 있도록 수정하였습니다! 반영 커밋 : fd70c6b

Copy link

@malibinYun malibinYun 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 +2 to +3

class LottoTickets(private val lottoTickets: List<Lotto> = emptyList()) {

Choose a reason for hiding this comment

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

일급 컬렉션으로 잘 분리해주셨네요 👍

Comment on lines +19 to +20
operator fun plus(lotto: LottoTickets): LottoTickets = LottoTickets(lottoTickets + lotto.lottoTickets)

Choose a reason for hiding this comment

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

plus 연산자 오버로딩을 잘 해주셨네요! 👍

Comment on lines +23 to +27
assertAll({
assertThat(lottoTickets.size).isEqualTo(2)
assertThat(lottoTickets[0].toList().map { it.toInt() }).isEqualTo(listOf(1, 2, 3, 4, 5, 6))
assertThat(lottoTickets[1].toList().map { it.toInt() }).isEqualTo(listOf(1, 11, 22, 33, 44, 45))
})

Choose a reason for hiding this comment

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

이렇게 각 검증문들의 람다를 넘겨주면 됩니다 :)

Suggested change
assertAll({
assertThat(lottoTickets.size).isEqualTo(2)
assertThat(lottoTickets[0].toList().map { it.toInt() }).isEqualTo(listOf(1, 2, 3, 4, 5, 6))
assertThat(lottoTickets[1].toList().map { it.toInt() }).isEqualTo(listOf(1, 11, 22, 33, 44, 45))
})
assertAll(
{ assertThat(lottoTickets.size).isEqualTo(2) },
{ assertThat(lottoTickets[0].toList().map { it.toInt() }).isEqualTo(listOf(1, 2, 3, 4, 5, 6)) },
{ assertThat(lottoTickets[1].toList().map { it.toInt() }).isEqualTo(listOf(1, 11, 22, 33, 44, 45)) },
)

Copy link
Author

Choose a reason for hiding this comment

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

방금 함수 정의 부분 열어보고 vararg을 전달하는 것으로 해당 내용에 대해서 이해하였습니다!

fun assertAll(vararg executables: () -> Unit) =
    assertAll(executables.toList().stream())

지금 제 코드처럼 하게 된다면 assertAll의 역할을 수행하지 못한다고 이해할 수 있었습니다!
알려주셔서 감사합니다!

@malibinYun malibinYun merged commit 4dddc5b into woowacourse:re4rk Feb 22, 2023
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