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단계] 공백 미션 제출합니다. #131

Open
wants to merge 42 commits into
base: junseo511
Choose a base branch
from

Conversation

junseo511
Copy link

셀프 체크리스트

  • 프로그램이 정상적으로 작동하는가?
  • 모든 테스트가 통과하는가?
  • 이전에 받은 피드백을 모두 반영하였는가?
  • 코딩 스타일 가이드를 준수하였는가?
    • IDE 코드 자동 정렬을 적용하였는가?
    • 린트 검사를 통과하였는가?
  • 프로그래밍 요구 사항을 준수하였는가?
  • README.md에 기능 목록을 정리하고 명확히 기술하였는가?

어떤 부분에 집중하여 리뷰해야 할까요?

  • 전반적인 테스트 코드의 가독성을 위해 GIVEN-WHEN-THEN 주석을 추가했습니다.
  • ProfitRate에 대해 암만 생각해봐도 View에는 primitive한 변수만 넘기고자 했으나...! 그렇게 하면 제가 만족할 수 있는 로직이 안나와서 domain model을 의존해서 분기처리 했습니다 🤣 그쪽이 입출력 객체를 구현하기 좀 더 유연해지는 것 같아요!!
  • Controller를 가볍게 만드는 과정이 생각보다 어렵네요 ㅠ 함수를 분리하는 것에 조금 더 신경써야겠어요! 입력, 출력, 로직 진행 이렇게 나누니까 조금 더 분리하기 편한 것 같네요!

< 크롱의 소중한 시간을 내어 리뷰해주셔서 감사합니다 🙇‍♀️ >

코드 리뷰 커뮤니케이션

📌 GitHub에서 PR에 댓글 남기는 방법

참고 자료

스크린샷

image

@junseo511 junseo511 changed the title Step2 [로또 2단계] 공백 미션 제출합니다. Feb 24, 2025
Copy link

@krrong krrong left a comment

Choose a reason for hiding this comment

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

안녕하세요 공백,
2단계를 정말 빠르게 반영해주셨네요!

다만 요구사항이 모두 컨트롤러에 구현되어있어요.
지금까지 배운 내용을 어떻게 적용할 수 있을까 다시 한 번 고민해보시고 리뷰요청 해주세요! 🤠

import lotto.model.ProfitStatus
import lotto.model.Rank
import lotto.service.LottoService
import lotto.model.WinningDiscriminator
import lotto.view.InputView
import lotto.view.OutputView

class LottoController(
Copy link

Choose a reason for hiding this comment

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

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1
수동으로 구매할 번호를 입력해 주세요.
1,2,3,4,5,6

위 처럼 입력하면 어떻게 될까요?
예상한 동작일까요? 🤔

Copy link
Author

Choose a reason for hiding this comment

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

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1
수동으로 구매할 번호를 입력해 주세요.
1,2,3,4,5,6

위 처럼 입력하면 어떻게 될까요? 예상한 동작일까요? 🤔

객체 생성의 순서를 조금 바꿔야 할 필요가 있을 것 같네요 🧐

val manualLottoQuantity = inputView.readManualLottoQuantity()

if (manualLottoQuantity > EMPTY_LOTTO_QUANTITY) outputView.printManualLottoNumbersGuide()
val manualLottos = List(manualLottoQuantity) { Lotto.from(inputView.readLottoNumbers()) }
Copy link

Choose a reason for hiding this comment

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

2단계 요구사항을 모두 컨트롤러에서 반영한 느낌이에요. 🤔

마치 자동차 경주와 1단계에서 배운 내용을 다 잊어버린 것처럼 보여요.
오늘 수업에서 배운 내용을 잘 기억해보시고 다시 구현해주시면 좋을 것 같습니다!

Copy link
Author

@junseo511 junseo511 Feb 27, 2025

Choose a reason for hiding this comment

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

2단계 요구사항을 모두 컨트롤러에서 반영한 느낌이에요. 🤔

마치 자동차 경주와 1단계에서 배운 내용을 다 잊어버린 것처럼 보여요. 오늘 수업에서 배운 내용을 잘 기억해보시고 다시 구현해주시면 좋을 것 같습니다!

아직 컨트롤러의 함수명을 어떻게 지어야 하고, 얼만큼의 부피를 가지고 뷰와 모델을 작동시켜야 하는지 어렵네요 🥲

Copy link

@krrong krrong left a comment

Choose a reason for hiding this comment

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

안녕하세요 공백!

2단계로 넘어오면서 그 경계가 조금 허물어졌다는 느낌이 드네요🥲
1단계에서 만들어둔 구조를 어떻게 하면 잘 활용할 수 있을지 고민해보면 좋겠어요.
코멘트 고민해보시고 어려운 점이나 의논이 필요한 부분은 dm 주셔도 됩니다! 🙇‍♂️

Comment on lines 6 to 8
fun add(lottoNumbers: List<Int>) {
lottoBundle.add(Lotto.from(lottoNumbers))
}
Copy link

Choose a reason for hiding this comment

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

자동 로또는 LottoMachine이 발급해주는데,
수동 로또는 왜 LottoWallet 스스로 추가를 하고 있을까요? 🤔

Copy link
Author

Choose a reason for hiding this comment

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

자동 로또는 LottoMachine이 발급해주는데, 수동 로또는 왜 LottoWallet 스스로 추가를 하고 있을까요? 🤔

🥲 위에서 답변을 남겼습니다... 어제의 코드 짠 자신을 반성중 🔨

Comment on lines 32 to 34
val winningLotto = getWinningNumbers()
val bonusLottoNumber = getBonusNumber()
val winningDiscriminator = WinningDiscriminator(winningLotto, bonusLottoNumber)
Copy link

Choose a reason for hiding this comment

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

현재 세 줄, getWinningNumbers 6줄, getBonusNumber 6줄
WinningDiscriminator 클래스 하나를 인스턴스 하기 위해 너무 많은 라인을 낭비하고 있지는 않나요? 🤔

함수는 하나의 일만 한다 라는 말이 정말 보너스 번호만 받아야 해, 당첨 번호만 받아야 해, 와 같은 시각으로 받아들여지지 않았으면 좋겠어요.
WinningDiscriminator를 초기화 하는 것도 하나의 일로 볼 수 있으니까요.

우리는 함수를 분리하는 것이지 분해하는 것이 아닙니다 😅

Copy link
Author

Choose a reason for hiding this comment

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

현재 세 줄, getWinningNumbers 6줄, getBonusNumber 6줄 WinningDiscriminator 클래스 하나를 인스턴스 하기 위해 너무 많은 라인을 낭비하고 있지는 않나요? 🤔

함수는 하나의 일만 한다 라는 말이 정말 보너스 번호만 받아야 해, 당첨 번호만 받아야 해, 와 같은 시각으로 받아들여지지 않았으면 좋겠어요. WinningDiscriminator를 초기화 하는 것도 하나의 일로 볼 수 있으니까요.

우리는 함수를 분리하는 것이지 분해하는 것이 아닙니다 😅

이 부분도 제가 컨트롤러를 너무 어렵게 생각했는가? 라는 생각을 했습니다.
절차별로 따라 각각 꼭 다시 필요한 객체를 반환하고,
그 절차만큼만 함수로 굵직하게 분리해볼까 합니다 🤔

@@ -0,0 +1,15 @@
package lotto.model

class LottoWallet {
Copy link

Choose a reason for hiding this comment

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

LottoWallet이 등장하게 된 배경은 무엇일까요?

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

LottoWallet이 등장하게 된 배경은 무엇일까요?

더 로또들의 추가와 읽기를 따로 관리해서 읽고 쓰는 작업에 용이한 객체가 있으면 편하겠다는 생각을 했습니다!


fun isContain(number: LottoNumber): Boolean = numbers.contains(number)

fun getRawNumbers(): List<Int> = this.numbers.map { it.number }
Copy link

Choose a reason for hiding this comment

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

getRawNumbersOutputView에서만 사용하고 있는데요!
도메인과 뷰의 분리가 잘 되어있지 않은 것처럼 보이네요 🤔

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

getRawNumbersOutputView에서만 사용하고 있는데요! 도메인과 뷰의 분리가 잘 되어있지 않은 것처럼 보이네요 🤔

정말로 뷰를 위한 도메인의 로직이 되어버렸네요 🥲
숫자를 출력하는 정도의 매핑은 '출력'을 위한 로직이니 아웃풋에서 진행해도 될거같다는 생각이 들어요

살짝 옮겨봐야겠어요
0c0a3ad

fun isHaveBonus(number: LottoNumber): Boolean = numbers.contains(number)
fun countMatchNumbers(otherLotto: Lotto): Int = numbers.count { number -> number in otherLotto.numbers }

fun isContain(number: LottoNumber): Boolean = numbers.contains(number)
Copy link

Choose a reason for hiding this comment

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

nit: is라는 prefix는 붙지 않아도 될 것 같아요 🤠

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

nit: is라는 prefix는 붙지 않아도 될 것 같아요 🤠

일반적으로 컬렉션이 가지고 있는 contains 라는 네이밍을 사용해봐도 좋을 것 같아요!!
66557d3

Comment on lines 10 to 11
val ALL_LOTTO_NUMBERS = (LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE)
private val CACHE = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) }
private val CACHE_LOTTO_NUMBER = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) }
Copy link

Choose a reason for hiding this comment

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

캐싱 전략을 올바르게 사용하고 있는지 고민해보면 좋겠어요 🤔

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

캐싱 전략을 올바르게 사용하고 있는지 고민해보면 좋겠어요 🤔

어떤 부분에 대한 답변인지 정확히 이해하지 못하겠어요...!!
제가 보고 아쉬운 부분이 있어서 코드를 고치고 테스트를 진행해봤습니다 :)

혹시 더 보완할 부분이 있다면 답변해주시면 감사하겠습니다 👍
ae35853

@@ -27,6 +27,6 @@ enum class Rank(
} ?: MISS
}

private fun isMiss(countOfMatch: Int): Boolean = countOfMatch <= 2
private fun isMiss(countOfMatch: Int): Boolean = countOfMatch < FIFTH.countOfMatch
Copy link

Choose a reason for hiding this comment

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

이전 리뷰에서 이 부분에 대한 의논이 부족했던 것 같아요.
이 함수가 필요한 이유가 무엇이었나요? 🤔

추가로 위의 from 함수는 return when 처럼 작성하여 간단하게 줄여볼 수 있을 것 같아요!

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

이전 리뷰에서 이 부분에 대한 의논이 부족했던 것 같아요. 이 함수가 필요한 이유가 무엇이었나요? 🤔

추가로 위의 from 함수는 return when 처럼 작성하여 간단하게 줄여볼 수 있을 것 같아요!

저도 리팩토링을 진행하면서 이 함수를 보고 잠깐 고민을 해봤는데요,
처음에는 when으로 인해 생기는 else를 지양하고 싶어서 위와 같이 분리를 했습니다.

하지만 오히려 가독성을 저해시키고 불필요하게 함수가 분리되는 문제가 생겨서
= when 으로 리팩토링을 해보면 어떨까 합니다
52e52ae

Copy link
Author

Choose a reason for hiding this comment

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

        fun from(
            countOfMatch: Int,
            matchBonus: Boolean,
        ): Rank =
            when {
                countOfMatch < FIFTH.countOfMatch -> MISS
                countOfMatch == SECOND.countOfMatch && matchBonus -> SECOND
                else ->
                    entries.find { rank ->
                        rank.countOfMatch == countOfMatch
                    } ?: MISS
            }

이런 느낌은 어떨까요?!

Comment on lines 11 to 15
private fun validateWinningNumberAndBonusNumberDuplicate() {
require(!isHaveBonusNumber(winningLotto)) {
"[ERROR] 우승 번호와 보너스 번호는 중복될 수 없습니다."
}
}
Copy link

Choose a reason for hiding this comment

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

유효성을 검증하는 것을 한 눈에 알아볼 수 있도록 함수로 분리하고 init 블럭에서 호출한다. 동의합니다.
하지만 함수에 붙여준 이름을 읽는 것보다 직접 작성된 코드를 읽는 것이 가독성이 더 좋다고 느껴지기도 해요 😅

validateWinningNumberAndBonusNumberDuplicate()
require(!lotto.contains(bonusNumber)) { ... }

유효성 검증이 많아질수록 init 블럭 하위에 유효성 검증 함수들이 늘어날테고, 정작 중요한 로직들을 찾아보기 위해서는 스크롤을 해야하기도 하겠죠?

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

유효성을 검증하는 것을 한 눈에 알아볼 수 있도록 함수로 분리하고 init 블럭에서 호출한다. 동의합니다. 하지만 함수에 붙여준 이름을 읽는 것보다 직접 작성된 코드를 읽는 것이 가독성이 더 좋다고 느껴지기도 해요 😅

validateWinningNumberAndBonusNumberDuplicate() require(!lotto.contains(bonusNumber)) { ... }

유효성 검증이 많아질수록 init 블럭 하위에 유효성 검증 함수들이 늘어날테고, 정작 중요한 로직들을 찾아보기 위해서는 스크롤을 해야하기도 하겠죠?

직접 작성된 코드가 좀 더 잘보일 때도 확실히 있는 것 같아요 👍 하지만 말씀드린 것처럼 유효성 검증 로직이 복잡해진다거나 하면 함수가 분리될텐데, 어떤 것은 분리하고 어떤 것은 남겨두면 코드를 작성하는데 있어서 일관성이 지켜지지 못할 것 같다는 생각이 들었어요 🥲

하지만 코틀린애서 권장되는 컨벤션으로 주요 로직은 위로 올라와야 한다는 부분은 지켜지지 못한 것 같아요! 유효성 함수를 init 블록에서 실행을 명시한 뒤, 구체적인 로직은 하단으로 내린다면 어떨까요?
adb6bab

}
}

fun getResult(lottos: List<Lotto>): Map<Rank, Int> {
Copy link

Choose a reason for hiding this comment

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

LottoWallet을 만들어주었는데 정작 컨트롤러에서는 값을 꺼내어서 전달하고 있네요 🤔

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

LottoWallet을 만들어주었는데 정작 컨트롤러에서는 값을 꺼내어서 전달하고 있네요 🤔

로또를 판별하는 객체가 로또의 저장소를 알아야 할까? 라는 의문이 들었었는데,
객체의 입장에서 본다면 지갑만 던져주고 알아서 작업이 진행되게 하는게 더 자연스러울 수도 있다는 생각이 드네요
7811eeb

Comment on lines 3 to 4
class LottoCashier(
val amount: Int,
private val amount: Int,
Copy link

Choose a reason for hiding this comment

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

현재의 구조는 클래스들간의 유기적인 관계가 조금 부족해 보여요.
LottoCashier라는 네이밍만 봐서는 돈을 받고 로또를 팔 것처럼 느껴지거든요. 😅

지금 수동로또는 LottoWallet이 스스로 만들고, LottoCashier는 로또를 몇 장 살 수 있는지만 알려주고, 그 결과와 숫자 생성기를 들고 달려가 어디선가 자동 로또를 만들어오고 있어요.

다음과 같이 변경해볼까요?
LottoCashier는 돈을 받고 로또를 판매한다. → LottoCashier는 필요한 로또를 LottoMachine에서 출력한다. → 어떤 LottoMachine을 사용할지는 LottoCashier가 정할 수 있다. → LottoCashier는 Lotto뭉치를 반환한다.

Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

현재의 구조는 클래스들간의 유기적인 관계가 조금 부족해 보여요. LottoCashier라는 네이밍만 봐서는 돈을 받고 로또를 팔 것처럼 느껴지거든요. 😅

지금 수동로또는 LottoWallet이 스스로 만들고, LottoCashier는 로또를 몇 장 살 수 있는지만 알려주고, 그 결과와 숫자 생성기를 들고 달려가 어디선가 자동 로또를 만들어오고 있어요.

다음과 같이 변경해볼까요? LottoCashier는 돈을 받고 로또를 판매한다. → LottoCashier는 필요한 로또를 LottoMachine에서 출력한다. → 어떤 LottoMachine을 사용할지는 LottoCashier가 정할 수 있다. → LottoCashier는 Lotto뭉치를 반환한다.

으으 제가 이 부분이 가장 내고 나서 안타까웠고 부끄러웠던 부분인데요,,,!
로또 지갑이 왜 혼자 로또를 만들지? 라는 생각을 시작으로 굉장히 많은 객체 수정을 진행했습니다 🥲

현재는 LottoCashier 가 아닌 LottoMarket 으로 네이밍의 변경을 진행했고,
여기서 로또 기계를 작동시키는 일 역시 진행하고 있습니다.

이렇게 발행된 로또는 로또 지갑에 다시 저장되고,
이 지갑 안의 로또로 우승의 판별을 진행하도록 리팩토링을 해보았습니다
5c7e75d

Copy link
Author

@junseo511 junseo511 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 10 to 11
val ALL_LOTTO_NUMBERS = (LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE)
private val CACHE = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) }
private val CACHE_LOTTO_NUMBER = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) }
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

캐싱 전략을 올바르게 사용하고 있는지 고민해보면 좋겠어요 🤔

어떤 부분에 대한 답변인지 정확히 이해하지 못하겠어요...!!
제가 보고 아쉬운 부분이 있어서 코드를 고치고 테스트를 진행해봤습니다 :)

혹시 더 보완할 부분이 있다면 답변해주시면 감사하겠습니다 👍
ae35853

@@ -1,7 +1,7 @@
package lotto.model

class Lotto private constructor(
val numbers: List<LottoNumber>,
private val numbers: List<LottoNumber>,
) {
init {
validateLottoNumbersCount(numbers)
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

놓친 부분이 있는데, validateLottoNumbersDuplicate 에서 필요하지 않은 부분도 보이네요 🤠

distinct() 로 한번에 묶는건 어떨까요? 제가 이 부분을 놓친 것 같아서요 😅
f328b98

fun isHaveBonus(number: LottoNumber): Boolean = numbers.contains(number)
fun countMatchNumbers(otherLotto: Lotto): Int = numbers.count { number -> number in otherLotto.numbers }

fun isContain(number: LottoNumber): Boolean = numbers.contains(number)
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

nit: is라는 prefix는 붙지 않아도 될 것 같아요 🤠

일반적으로 컬렉션이 가지고 있는 contains 라는 네이밍을 사용해봐도 좋을 것 같아요!!
66557d3


fun isContain(number: LottoNumber): Boolean = numbers.contains(number)

fun getRawNumbers(): List<Int> = this.numbers.map { it.number }
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

getRawNumbersOutputView에서만 사용하고 있는데요! 도메인과 뷰의 분리가 잘 되어있지 않은 것처럼 보이네요 🤔

정말로 뷰를 위한 도메인의 로직이 되어버렸네요 🥲
숫자를 출력하는 정도의 매핑은 '출력'을 위한 로직이니 아웃풋에서 진행해도 될거같다는 생각이 들어요

살짝 옮겨봐야겠어요
0c0a3ad

@@ -27,6 +27,6 @@ enum class Rank(
} ?: MISS
}

private fun isMiss(countOfMatch: Int): Boolean = countOfMatch <= 2
private fun isMiss(countOfMatch: Int): Boolean = countOfMatch < FIFTH.countOfMatch
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

이전 리뷰에서 이 부분에 대한 의논이 부족했던 것 같아요. 이 함수가 필요한 이유가 무엇이었나요? 🤔

추가로 위의 from 함수는 return when 처럼 작성하여 간단하게 줄여볼 수 있을 것 같아요!

저도 리팩토링을 진행하면서 이 함수를 보고 잠깐 고민을 해봤는데요,
처음에는 when으로 인해 생기는 else를 지양하고 싶어서 위와 같이 분리를 했습니다.

하지만 오히려 가독성을 저해시키고 불필요하게 함수가 분리되는 문제가 생겨서
= when 으로 리팩토링을 해보면 어떨까 합니다
52e52ae

Comment on lines 3 to 4
class LottoCashier(
val amount: Int,
private val amount: Int,
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

현재의 구조는 클래스들간의 유기적인 관계가 조금 부족해 보여요. LottoCashier라는 네이밍만 봐서는 돈을 받고 로또를 팔 것처럼 느껴지거든요. 😅

지금 수동로또는 LottoWallet이 스스로 만들고, LottoCashier는 로또를 몇 장 살 수 있는지만 알려주고, 그 결과와 숫자 생성기를 들고 달려가 어디선가 자동 로또를 만들어오고 있어요.

다음과 같이 변경해볼까요? LottoCashier는 돈을 받고 로또를 판매한다. → LottoCashier는 필요한 로또를 LottoMachine에서 출력한다. → 어떤 LottoMachine을 사용할지는 LottoCashier가 정할 수 있다. → LottoCashier는 Lotto뭉치를 반환한다.

으으 제가 이 부분이 가장 내고 나서 안타까웠고 부끄러웠던 부분인데요,,,!
로또 지갑이 왜 혼자 로또를 만들지? 라는 생각을 시작으로 굉장히 많은 객체 수정을 진행했습니다 🥲

현재는 LottoCashier 가 아닌 LottoMarket 으로 네이밍의 변경을 진행했고,
여기서 로또 기계를 작동시키는 일 역시 진행하고 있습니다.

이렇게 발행된 로또는 로또 지갑에 다시 저장되고,
이 지갑 안의 로또로 우승의 판별을 진행하도록 리팩토링을 해보았습니다
5c7e75d

@@ -0,0 +1,15 @@
package lotto.model

class LottoWallet {
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

LottoWallet이 등장하게 된 배경은 무엇일까요?

더 로또들의 추가와 읽기를 따로 관리해서 읽고 쓰는 작업에 용이한 객체가 있으면 편하겠다는 생각을 했습니다!

Comment on lines 6 to 8
fun add(lottoNumbers: List<Int>) {
lottoBundle.add(Lotto.from(lottoNumbers))
}
Copy link
Author

Choose a reason for hiding this comment

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

자동 로또는 LottoMachine이 발급해주는데, 수동 로또는 왜 LottoWallet 스스로 추가를 하고 있을까요? 🤔

🥲 위에서 답변을 남겼습니다... 어제의 코드 짠 자신을 반성중 🔨

}
}

fun getResult(lottos: List<Lotto>): Map<Rank, Int> {
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

LottoWallet을 만들어주었는데 정작 컨트롤러에서는 값을 꺼내어서 전달하고 있네요 🤔

로또를 판별하는 객체가 로또의 저장소를 알아야 할까? 라는 의문이 들었었는데,
객체의 입장에서 본다면 지갑만 던져주고 알아서 작업이 진행되게 하는게 더 자연스러울 수도 있다는 생각이 드네요
7811eeb

Comment on lines 11 to 15
private fun validateWinningNumberAndBonusNumberDuplicate() {
require(!isHaveBonusNumber(winningLotto)) {
"[ERROR] 우승 번호와 보너스 번호는 중복될 수 없습니다."
}
}
Copy link
Author

@junseo511 junseo511 Feb 28, 2025

Choose a reason for hiding this comment

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

유효성을 검증하는 것을 한 눈에 알아볼 수 있도록 함수로 분리하고 init 블럭에서 호출한다. 동의합니다. 하지만 함수에 붙여준 이름을 읽는 것보다 직접 작성된 코드를 읽는 것이 가독성이 더 좋다고 느껴지기도 해요 😅

validateWinningNumberAndBonusNumberDuplicate() require(!lotto.contains(bonusNumber)) { ... }

유효성 검증이 많아질수록 init 블럭 하위에 유효성 검증 함수들이 늘어날테고, 정작 중요한 로직들을 찾아보기 위해서는 스크롤을 해야하기도 하겠죠?

직접 작성된 코드가 좀 더 잘보일 때도 확실히 있는 것 같아요 👍 하지만 말씀드린 것처럼 유효성 검증 로직이 복잡해진다거나 하면 함수가 분리될텐데, 어떤 것은 분리하고 어떤 것은 남겨두면 코드를 작성하는데 있어서 일관성이 지켜지지 못할 것 같다는 생각이 들었어요 🥲

하지만 코틀린애서 권장되는 컨벤션으로 주요 로직은 위로 올라와야 한다는 부분은 지켜지지 못한 것 같아요! 유효성 함수를 init 블록에서 실행을 명시한 뒤, 구체적인 로직은 하단으로 내린다면 어떨까요?
adb6bab

Copy link

@krrong krrong left a comment

Choose a reason for hiding this comment

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

안녕하세요 공백!

리뷰하면서 코드가 나아지는 게 눈에 보이네요 👍
고민해보면 좋을 부분에 대해 코멘트를 남겨놨으니 확인해주시고 리뷰요청 해주시면 됩니다!
어려운 부분이나 의논이 필요한 부분은 dm이나 코멘트로 알려주세요 🙇‍♂️


val ALL_LOTTO_NUMBERS = (LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE)
private val CACHE = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) }
private val CACHE_LOTTO_NUMBER = mutableMapOf<Int, LottoNumber>()
Copy link

Choose a reason for hiding this comment

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

현재 LottoMachinegetAutoNumbers함수에서 로또의 최대 최소 값을 가져와 리스트를 만들고,
그를 바탕으로 로또 번호를 만들고 있는데요.

사실 LottoMachine은 로또의 최대, 최소 값에 대한 관심이 없을겁니다.
단지 모든 로또 번호 중에서 무작위로 값을 가져와야하죠.

이를 위해 LottoNumber가 캐싱을 하고 있는 인스턴스들 전체를 반환하도록 만들어주고, LottoMachine에서 가져다 사용하면,
함수의 깊이 하나가 줄어들게 되고, 캐싱 전략을 적절하게 사용하는 것이 아닐까 생각을 해봤어요.

공백은 어떻게 생각하나요?

Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

현재 LottoMachinegetAutoNumbers함수에서 로또의 최대 최소 값을 가져와 리스트를 만들고, 그를 바탕으로 로또 번호를 만들고 있는데요.

사실 LottoMachine은 로또의 최대, 최소 값에 대한 관심이 없을겁니다. 단지 모든 로또 번호 중에서 무작위로 값을 가져와야하죠.

이를 위해 LottoNumber가 캐싱을 하고 있는 인스턴스들 전체를 반환하도록 만들어주고, LottoMachine에서 가져다 사용하면, 함수의 깊이 하나가 줄어들게 되고, 캐싱 전략을 적절하게 사용하는 것이 아닐까 생각을 해봤어요.

공백은 어떻게 생각하나요?

정말로 로또머신이 모든 숫자들을 알 필요는 없겠네요! 그저 로또 넘버에게 너네 다 줘봐! 하고서 골라주기만 하는 방향이 크롱의 말씀대로 더 합당한 것 같아요 :)

숫자는 45개 밖에 없으니 미리 CACHE_LOTTO_NUMBER를 캐싱해두고, cachedLottoNumbers를 lazy로 선언해서 사용할 때 list로 모든 숫자를 불러오는 방법도 괜찮다고 생각해서 그 방향으로 구현해봤습니다 👍
f842ceb

private fun validateLottoNumbersCount(numbers: List<LottoNumber>) {
fun countMatchNumbers(other: Lotto): Int = numbers.count { number -> number in other.numbers }

fun contains(number: LottoNumber): Boolean = numbers.contains(number)
Copy link

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 27
): Rank =
when {
isMiss(countOfMatch) -> return MISS
countOfMatch == SECOND.countOfMatch && matchBonus -> return SECOND
countOfMatch < FIFTH.countOfMatch -> MISS
countOfMatch == SECOND.countOfMatch && matchBonus -> SECOND
else ->
entries.find { rank ->
rank.countOfMatch == countOfMatch
} ?: throw IllegalArgumentException("[ERROR] 유효하지 않은 랭크입니다")
}
Copy link

Choose a reason for hiding this comment

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

처음에는 when으로 인해 생기는 else를 지양하고 싶어서 위와 같이 분리를 했습니다.

else를 지양하는 이유는 무엇일까요?

enum class를 사용하면 when에서 else 분기문 없이 처리가 가능한데요.
이렇게 처리하도록 한 이유에 대해서 고민해보는 것도 좋겠어요 🤠


개인적으로는 현재 코드가 더 가독성 넘치고 좋은 것 같아요 👍

Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

else를 지양하는 이유는 무엇일까요?

enum class를 사용하면 when에서 else 분기문 없이 처리가 가능한데요. 이렇게 처리하도록 한 이유에 대해서 고민해보는 것도 좋겠어요 🤠

개인적으로는 현재 코드가 더 가독성 넘치고 좋은 것 같아요 👍

  • enum class의 when에서 각각의 케이스를 모두 분기처리하기가 껄끄러웠습니다...! 충분히 대소비교로 가능할 것 같은 로직을 노가다를 하는 느낌이었달까요...?!
  • else를 사용하는 것을 꺼린 이유는 코드를 읽는 사람이 else가 어떤 조건인지 바로 알지 못할 수도 있다고 생각했어요! 객체 지향 생활 체조 원칙에서도 else를 지양하라는 말이 있듯요...!

그거랑 별개로 이 로직은 이것저것 해보니 지금이 훨씬 깔끔해서 만족스럽네요 🤣

Comment on lines 7 to 8
class LottoMachine {
fun getLottos(lottoQuantity: Int): Lottos {
val lottos = List(lottoQuantity) { Lotto.from(getLottoNumbers()) }
return Lottos(lottos)
}
fun getAutoLottos(quantity: Int): List<Lotto> = List(quantity) { Lotto.from(getAutoNumbers()) }
Copy link

Choose a reason for hiding this comment

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

LottoMachine은 로또를 생성하는 일을 하는데요.
현재로서는 하나의 기계가 자동, 수동로또 모두 만들고 있어요.
하지만 자동이냐 수동이냐만 다를뿐, 로또를 만든다는 행위는 같죠.

우리는 이러한 공통 행위를 인터페이스로 추상화할 수 있고,
어떠한 방법으로 로또를 생성할 것인지는 구현체에서 결정하도록 변경할 수 있겠어요 🤠

힌트: LottoMarket이 더 간단해질 수 있음!

interface LottoMachine {
    fun getLottos()
}
// 로또자동생성
class AutoLottoMachine(): LottoMachine
// 로또수동생성
class ManualLottoMachine(): LottoMachine

Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

LottoMachine은 로또를 생성하는 일을 하는데요. 현재로서는 하나의 기계가 자동, 수동로또 모두 만들고 있어요. 하지만 자동이냐 수동이냐만 다를뿐, 로또를 만든다는 행위는 같죠.

우리는 이러한 공통 행위를 인터페이스로 추상화할 수 있고, 어떠한 방법으로 로또를 생성할 것인지는 구현체에서 결정하도록 변경할 수 있겠어요 🤠

힌트: LottoMarket이 더 간단해질 수 있음!

interface LottoMachine {
    fun getLottos()
}
// 로또자동생성
class AutoLottoMachine(): LottoMachine
// 로또수동생성
class ManualLottoMachine(): LottoMachine

안그래도 어제 잡담-질답방에 interface와 class에 관한 자료를 게시했다가 무수한 갈고리로 애정을 받았는데요 😅
고통받은 김에 한 번 연습해보는 것도 좋을 것 같네요 👍
추후 반자동 로또가 생성된다면 이 역시 SemiAutoMachine으로 확장도 가능할테죠!!

근데 자동 로또와 수동 로또를 얻는 과정에서 필요한 값이 너무 달라서
generate 함수에 필요한 변수로 아래 커밋과 같이 받았는데, 이 방법도 괜찮은지 고민이 조금 되네요 🤔
bee65d8

Comment on lines +4 to +5
private val _lottos: MutableList<Lotto> = mutableListOf()
val lottos: List<Lotto> get() = _lottos.toList()
Copy link

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.

이러한 형태로 반환하는 이유가 무엇인가요? 🤔

로또 목록의 추가가 유연하도록 MutableList를 사용하였는데, 이렇게 사용했을 경우에는 외부에서 원하지 않는 읽기 및 쓰기가 발생할 수 있다고 생각했습니다. 따라서 내부에서 사용하는 로또목록을 백킹프로퍼티로 두고, 외부에서는 리스트로만 읽을 수 있게 사용하고 싶었어요!

Comment on lines 3 to 4
class LottoMarket(
private val amount: Int,
Copy link

Choose a reason for hiding this comment

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

금액이라는 원시값도 포장해볼까요? 그러면 어떤 장점이 있을까요? 🤔

Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

금액이라는 원시값도 포장해볼까요? 그러면 어떤 장점이 있을까요? 🤔

현재 LottoMarket에서 진행되는 유효값 검증을 Amount로 옮길 수 있을 것 같아요!
인스턴스가 생성되는 비용을 줄이기 위해 value class 를 사용해도 괜찮을 것 같다는 생각도 들구요 🤔

하지만 이렇게 만든 포장된 원시값은 Lotto라는 도메인에서만 진행되어 다른데로 확장하기가 조금 어렵다는 생각도 드네요!!
대신 LottoMarket이 훨씬 가벼워지는 장점이 있어요 👍
ff9bfa2

Copy link

Choose a reason for hiding this comment

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

머지를 하려고 하다가 이 부분은 조금 더 고민이 필요할 것 같아 코멘트와 RC를 남겨드렸어요.
다만 스스로 생각하기에는 어려운 내용일 수도 있으니,
혼자 해결해야한다고 너무 매몰되지는 마시고 언제든 도움요청 하셔도 됩니다👍
블랙잭 미션후에 제출해주셔도 좋아요!

Copy link

Choose a reason for hiding this comment

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

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1

위 처럼 입력하면 어떻게 될까요?
예상한 동작일까요? 🤔

추가로 요구사항 중 하나인 예외 처리를 통해 에러가 발생하지 않도록 한다.에 대해서도 고민해보면 좋을 것 같아요

Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1

위 처럼 입력하면 어떻게 될까요? 예상한 동작일까요? 🤔

컨트롤러를 구현하다가 보면 객체 순서를 맞추다가 하나씩 자꾸 놓치는 것 같아요 🥲
이 부분은 Amount를 포장하면서 자연스럽게 해결이 되었네요 👍
36b8292

Copy link
Author

@junseo511 junseo511 Mar 3, 2025

Choose a reason for hiding this comment

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

추가로 요구사항 중 하나인 예외 처리를 통해 에러가 발생하지 않도록 한다.에 대해서도 고민해보면 좋을 것 같아요

리뷰를 읽고 ValidateResult라는 sealed class를 만들고 Success<T> 로 반환값을 감싸서 만들어보려고 했는데,
생각보다 많은 부분을 수정해야 하더라구요 🥲 이번 수정 기간 안에는 완성하지 못할 것 같아서 블랙잭에서 시도해보겠습니다 ‼️

image

조만간 방과후에서 획득당한(?) 주제이기도 하네요 🤣
다음번엔 여유가 생길때 과제 힌트의 구현사항도 하나씩 뿌셔봐야겠어요 😇

Copy link
Author

@junseo511 junseo511 left a comment

Choose a reason for hiding this comment

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

2주라는 짧은 시간이었지만, 크롱 덕분에 배워갈 수 있는 점도 많았습니다 🚀
역할을 해낼 수 있는 객체에 조금 더 알아가는 시간이 되었는데요!!

중간에 졸업식으로 며칠 허둥지둥할 때에도 항상 꼼꼼하게 리뷰해주셔서
더 열심히 수정하고 완성도 높은 코드로 마칠 수 있었던 것 같습니다.

먼 타지에서도 즐거운 안드로이드 생활 되세요...! 감사합니다 😘


val ALL_LOTTO_NUMBERS = (LOTTO_NUMBER_MIN_RANGE..LOTTO_NUMBER_MAX_RANGE)
private val CACHE = ALL_LOTTO_NUMBERS.associateWith { LottoNumber(it) }
private val CACHE_LOTTO_NUMBER = mutableMapOf<Int, LottoNumber>()
Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

현재 LottoMachinegetAutoNumbers함수에서 로또의 최대 최소 값을 가져와 리스트를 만들고, 그를 바탕으로 로또 번호를 만들고 있는데요.

사실 LottoMachine은 로또의 최대, 최소 값에 대한 관심이 없을겁니다. 단지 모든 로또 번호 중에서 무작위로 값을 가져와야하죠.

이를 위해 LottoNumber가 캐싱을 하고 있는 인스턴스들 전체를 반환하도록 만들어주고, LottoMachine에서 가져다 사용하면, 함수의 깊이 하나가 줄어들게 되고, 캐싱 전략을 적절하게 사용하는 것이 아닐까 생각을 해봤어요.

공백은 어떻게 생각하나요?

정말로 로또머신이 모든 숫자들을 알 필요는 없겠네요! 그저 로또 넘버에게 너네 다 줘봐! 하고서 골라주기만 하는 방향이 크롱의 말씀대로 더 합당한 것 같아요 :)

숫자는 45개 밖에 없으니 미리 CACHE_LOTTO_NUMBER를 캐싱해두고, cachedLottoNumbers를 lazy로 선언해서 사용할 때 list로 모든 숫자를 불러오는 방법도 괜찮다고 생각해서 그 방향으로 구현해봤습니다 👍
f842ceb

Comment on lines 7 to 8
class LottoMachine {
fun getLottos(lottoQuantity: Int): Lottos {
val lottos = List(lottoQuantity) { Lotto.from(getLottoNumbers()) }
return Lottos(lottos)
}
fun getAutoLottos(quantity: Int): List<Lotto> = List(quantity) { Lotto.from(getAutoNumbers()) }
Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

LottoMachine은 로또를 생성하는 일을 하는데요. 현재로서는 하나의 기계가 자동, 수동로또 모두 만들고 있어요. 하지만 자동이냐 수동이냐만 다를뿐, 로또를 만든다는 행위는 같죠.

우리는 이러한 공통 행위를 인터페이스로 추상화할 수 있고, 어떠한 방법으로 로또를 생성할 것인지는 구현체에서 결정하도록 변경할 수 있겠어요 🤠

힌트: LottoMarket이 더 간단해질 수 있음!

interface LottoMachine {
    fun getLottos()
}
// 로또자동생성
class AutoLottoMachine(): LottoMachine
// 로또수동생성
class ManualLottoMachine(): LottoMachine

안그래도 어제 잡담-질답방에 interface와 class에 관한 자료를 게시했다가 무수한 갈고리로 애정을 받았는데요 😅
고통받은 김에 한 번 연습해보는 것도 좋을 것 같네요 👍
추후 반자동 로또가 생성된다면 이 역시 SemiAutoMachine으로 확장도 가능할테죠!!

근데 자동 로또와 수동 로또를 얻는 과정에서 필요한 값이 너무 달라서
generate 함수에 필요한 변수로 아래 커밋과 같이 받았는데, 이 방법도 괜찮은지 고민이 조금 되네요 🤔
bee65d8

Comment on lines 3 to 4
class LottoMarket(
private val amount: Int,
Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

금액이라는 원시값도 포장해볼까요? 그러면 어떤 장점이 있을까요? 🤔

현재 LottoMarket에서 진행되는 유효값 검증을 Amount로 옮길 수 있을 것 같아요!
인스턴스가 생성되는 비용을 줄이기 위해 value class 를 사용해도 괜찮을 것 같다는 생각도 들구요 🤔

하지만 이렇게 만든 포장된 원시값은 Lotto라는 도메인에서만 진행되어 다른데로 확장하기가 조금 어렵다는 생각도 드네요!!
대신 LottoMarket이 훨씬 가벼워지는 장점이 있어요 👍
ff9bfa2

Copy link
Author

@junseo511 junseo511 Mar 2, 2025

Choose a reason for hiding this comment

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

구입금액을 입력해 주세요.
1400
수동으로 구매할 로또 수를 입력해 주세요.
1

위 처럼 입력하면 어떻게 될까요? 예상한 동작일까요? 🤔

컨트롤러를 구현하다가 보면 객체 순서를 맞추다가 하나씩 자꾸 놓치는 것 같아요 🥲
이 부분은 Amount를 포장하면서 자연스럽게 해결이 되었네요 👍
36b8292

Copy link
Author

@junseo511 junseo511 Mar 3, 2025

Choose a reason for hiding this comment

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

추가로 요구사항 중 하나인 예외 처리를 통해 에러가 발생하지 않도록 한다.에 대해서도 고민해보면 좋을 것 같아요

리뷰를 읽고 ValidateResult라는 sealed class를 만들고 Success<T> 로 반환값을 감싸서 만들어보려고 했는데,
생각보다 많은 부분을 수정해야 하더라구요 🥲 이번 수정 기간 안에는 완성하지 못할 것 같아서 블랙잭에서 시도해보겠습니다 ‼️

image

조만간 방과후에서 획득당한(?) 주제이기도 하네요 🤣
다음번엔 여유가 생길때 과제 힌트의 구현사항도 하나씩 뿌셔봐야겠어요 😇

Copy link

@krrong krrong left a comment

Choose a reason for hiding this comment

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

안녕하세요 공백!
리뷰 반영하느라 고생하셨어요 👏

고통받은 김에 한 번 연습해보는 것도 좋을 것 같다고 하셔서,
추상화한 LottoMachine을 잘 활용할 수 있도록 코멘트를 남겨드렸어요 🤠
고민해보시고 어려우시면 dm이나 코멘트로 알려주시면 됩니다!

Comment on lines +3 to +5
@JvmInline
value class Amount(
val value: Int,
Copy link

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 +7
interface LottoMachine {
fun generate(
manualTicket: List<List<Int>> = emptyList(),
quantity: Int = manualTicket.size,
): List<Lotto>
Copy link

Choose a reason for hiding this comment

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

근데 자동 로또와 수동 로또를 얻는 과정에서 필요한 값이 너무 달라서
generate 함수에 필요한 변수로 아래 커밋과 같이 받았는데, 이 방법도 괜찮은지 고민이 조금 되네요 🤔

디폴트 값을 넘기면 어떤 문제가 발생할 수 있을까요?

인터페이스로 추상화하는 이유가 무엇일까에 대해서 먼저 고민해봐면 좋을 것 같은데요.
우리는 인터페이스로 추상화함으로써 역할과 구현을 구분할 수 있고, 이를 통해 유연성을 가질 수 있어요.

공백이 작성해준 방법은 manualTicket: List<List<Int>> = emptyList() 로 선언되어 있고,
구현에 대한 내용이 인터페이스에 정의되어 있다고 볼 수 있죠.

만약 반자동 발매기가 생긴다면 어떻게 해야 할까요? 이 역시도 인터페이스의 수정이 일어나야 할까요?
로또를 생성하는 구체적인 방법에 대해서는 구현체가 결정할 일입니다.

Comment on lines +15 to +18
fun buy(
manualNumbers: List<List<Int>> = emptyList(),
lottoWallet: LottoWallet,
) {
Copy link

Choose a reason for hiding this comment

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

LottoMarket에서 로또를 구매하기 위해서는 지갑을 주고 거기에 로또를 담아주는 행위는 어색하지 않나요?
로또를 구매하니 LottoWallet을 돌려주는 게 더 명확해 보이네요 🤔

Comment on lines +3 to +8
class LottoMarket(
private val amount: Amount,
private val manualQuantity: Int,
private val manualLottoMachine: ManualLottoMachine = ManualLottoMachine(),
private val autoLottoMachine: AutoLottoMachine = AutoLottoMachine(),
) {
Copy link

Choose a reason for hiding this comment

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

LottoMarketManualLottoMachineAutoLottoMachine를 모두 가지고 있어야 할까요?
추상화 한 LottoMachine을 잘 사용하지 못하고 있는데요, 어떤 기계를 사용할지는 사용하는 곳에서 정할 수도 있습니다.

의존성을 주입하는 방법은 대표적으로 3가지가 있으니 한 번 찾아보시고,
어떤 기계를 사용할까를 어떻게 사용하는 곳에서 결정할 수 있을지 고민해보면 좋겠어요 🤠

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