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

[로또] 이인협 미션 제출합니다. #97

Open
wants to merge 24 commits into
base: main
Choose a base branch
from

Conversation

cucumber99
Copy link

기능 구현 목록

입력

  • 구입 금액을 입력받는다.
  • 당첨 번호를 입력받는다.
  • 보너스 번호를 입력받는다.

출력

  • 생성된 로또 번호를 출력한다.
    • 오름차순으로 출력
  • 당첨 내역을 출력한다.
    • 상금과 개수를 출력한다.
    • 상금 출력시 ','를 천의 자리마다 배치한다.
    • 당첨 통계와 결과 출력 사이에 '---'를 배치한다.
  • 수익률을 출력한다.

로또

  • 입력 받은 금액을 바탕으로 로또 개수를 계산한다.
    • 구입금액 / 1000(로또 1장의 가격)
  • 계산된 개수만큼 로또 번호를 생성한다.
    • 1~45 사이의 중복되지 않는 자연수 6개 (pickUniqueNumberInRange)
  • 로또 번호와 당첨 번호를 비교하여 순위를 계산한다.
    • 1등(6개), 2등(5개+보너스), 3등(5개), 4등(4개), 5등(3개)
  • 수익률을 계산한다.
    • 수익률 = (당첨 금액/구매 금액)

예외 처리

  • 당첨 번호, 보너스 번호를 입력 시, 숫자가 아닌 경우
  • 금액이 1000원 단위가 아닌 경우
  • 금액이 숫자가 아닌 경우
  • 당첨 번호의 숫자 범위가 1~45 사이가 아닌 경우
  • 당첨 번호를 입력할 때 쉼표 기준이 아닌 경우
  • 당첨 번호에 중복 입력된 경우

학습 메모

Copy link

@LeeYongIn0517 LeeYongIn0517 left a comment

Choose a reason for hiding this comment

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

3주차도 수고하셨습니다...! 함수 깊이도 모두 3 미만으로 잘 작성하신 것 같습니다! 4주차도 화이팅이에요

import lotto.util.ErrorMessage

@JvmInline
value class BonusNumber(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로 BonusNumber를 작성하신 이유가 궁금합니다..!

Copy link

Choose a reason for hiding this comment

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

전 value class가 뭔지 몰라서 답변 스윽 기다려봅니당 ㅎㅎ

Copy link
Author

Choose a reason for hiding this comment

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

value class는 이번 미션에서 저도 처음 알게 된 키워드입니다 ㅎㅎ
코틀린에서는 특정 비즈니스값을 담는 객체인 Value Object를 value class를 통해 구현한다고 하더라고요.
특히 이 미션에서 로또 번호나 보너스 번호같은 원시 타입의 값을 래핑하기 위해서 사용해보았습니다.
이렇게 하면 해당 객체를 사용할 때 객체를 제거하고 객체의 프로퍼티로 대체한다고 합니다.

근데 저도 처음 접한 문법이라서 제대로 된 활용은 하지 못한 느낌이 없지 않아 있네요..하하

}
}

private fun tryGettingWinningLottoNumbers(): List<Int> {

Choose a reason for hiding this comment

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

tryGettingWinningLottoNumbers라는 함수이름에서 try가 들어가면 뭔가 여러번 시도하는 것 같은 인상을 주는데, 정작 try문을 갖고 있지 않고 try문 내부에 들어가는 함수인게 어색합니다! 목적이 혼동될 수 있으니 단순하게 현재진행도 빼고 getWinningLottoNumberMessage로 바꿔도 좋을 거 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

네이밍 이슈는 미션을 거듭해도 고쳐지지 않네요
좋은 지적 감사합니다.. 앞으로 네이밍에 조금 더 신중을 가해보겠습니다ㅠㅠ

Copy link

@wjdrjs00 wjdrjs00 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 +79 to +82
private fun calculateRank(lotto: Lotto, bonusNumber: BonusNumber, lottoMachine: LottoMachine): Map<RankType, Int> {
val rankCalculator = RankCalculator(lotto, bonusNumber, lottoMachine)
return rankCalculator.calculateRank()
}
Copy link

Choose a reason for hiding this comment

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

사소하지만 이 두 방식중 어떤 방식이 더 가독성이 좋아보이는지 인협님 의견이 궁금합니다!!🤔
전 2개까지는 괜찮은거 같은데 3개부터는 줄바꿈해주는 방식이 더 좋았던거 같아요!!

Suggested change
private fun calculateRank(lotto: Lotto, bonusNumber: BonusNumber, lottoMachine: LottoMachine): Map<RankType, Int> {
val rankCalculator = RankCalculator(lotto, bonusNumber, lottoMachine)
return rankCalculator.calculateRank()
}
private fun calculateRank(
lotto: Lotto,
bonusNumber: BonusNumber,
lottoMachine: LottoMachine
): Map<RankType, Int> {
val rankCalculator = RankCalculator(lotto, bonusNumber, lottoMachine)
return rankCalculator.calculateRank()
}

Copy link
Author

Choose a reason for hiding this comment

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

이런 부분까지는 신경 쓰지 못했던 것 같네요
꼼꼼한 지적 감사합니다 ㅎㅎ

import lotto.util.ErrorMessage

@JvmInline
value class BonusNumber(val number: Int) {
Copy link

Choose a reason for hiding this comment

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

전 value class가 뭔지 몰라서 답변 스윽 기다려봅니당 ㅎㅎ

}

companion object {
const val LOTTO_SIZE = 6
Copy link

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.

처음에는 private으로 지정했다가 외부에서도 상수를 사용하기 위해 접근 지정자를 변경했어요
사용하는 의도가 같아서 Lotto 클래스에 선언해 두었던 것을 그대로 사용했습니다

Comment on lines +3 to +27
object InputValidator {
fun validateInput(input: String) {
validateNull(input)
validateDigit(input)
validateNatural(input)
}

private fun validateNull(input: String) {
require(input.trim().isNotEmpty()) {
ErrorMessage.INPUT_NULL.getMessage()
}
}

private fun validateDigit(input: String) {
require(input.toIntOrNull() != null) {
ErrorMessage.INPUT_DIGIT.getMessage()
}
}

private fun validateNatural(input: String) {
require(input.trim().toInt() > 0) {
ErrorMessage.INPUT_NATURAL.getMessage()
}
}
}
Copy link

Choose a reason for hiding this comment

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

프리코스 하면서 입력에 대한 검증을 어디서 가져가야할지 항상 고민이 남더라고요,, 입력값이니 inputView에서 가져가여하나? 아니면 입력은 단순 입력값만 다루고 이를 받아 사용하는 클래스에서 가져가야하나??가 고민이 남았던거 같아요! 저는 1~3주차는 후자에서 검증을 하는 방식을 가져갔는데 이제와 생각해보니 과연 입력값에 대한 검증이 도메인 비즈니스로직이 가져야 하는 로직인가?? 생각이 들어 그럼 입력에서 가져가야하는게 맞는건가? 고민이 되는거 같습니다!!
그런 의미에서 인협님이 따로 InputValidator를 정의해서 입력에 대한 검증을 가져가신 이유??가 궁금합니다!!

Copy link
Author

Choose a reason for hiding this comment

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

저와 같은 고민을 거듭 하신 것 같네요
제 경우에는 처음에 입력값에 대한 모든 검증을 View에서 책임지게끔 코드를 작성했는데,
실제 모바일 개발 과정에서 입력값 검증을 UI 부분에서는 하지 않지 않나? 라는 생각이 문득 들어서
지난 미션부터 이런 저런 방식을 시도해보고 있습니다.

이번 미션에서는 객체들이 사용할 값이 모두 정수 타입이기 때문에
View에서는 입력된 문자열에 대해 간단한 검사만 한 뒤에 값을 객체로 넘기고,
비즈니스에 종속적인 부분 (로또 숫자 범위, 로또 숫자 개수) 은 객체 내부에서 처리하게끔 했습니다.

Comment on lines +6 to +23
object InputView {
private const val SEPARATOR = ","

private fun getUserInput(): String = Console.readLine()

fun getSingleDigit(): Int {
val input = getUserInput()
InputValidator.validateInput(input)
return input.toInt()
}

fun getMultipleDigit(): List<Int> {
val input = getUserInput()
return input.split(SEPARATOR).map {
InputValidator.validateInput(it)
it.toInt()
}
}
Copy link

Choose a reason for hiding this comment

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

인협님 프젝 구조를 보면 mvc 구조를 가져가고 있으신거 같은데 input,output view를 object로 가져가신 이유가 궁금합니다!!!
저도 요번 프리코스를 통해 생각해게된 내용인데, object를 통해 하나의 인스턴스로 가져가는게 mvc에 맞는 방식일까? 고민이 들었습니다. mvc는 model과 view는 서로 모르는 상태에서 controller를 통해 상호작용하는것이 핵심이라 생각하는데 view를 object로 사용하면 사실 정의만 안했을 뿐이지 model에서 가져가 쓸수 있는 상태가 아닌가? 사실상 mvc가 아닌 mc 인거 같은데?? 하는 생각에 개인적으로 object를 사용하는 방식은 mvc구조에서 잘못된 방식이겠다는 생각이 들어서 질문을 남겨봅니다!!🙇🏻‍♂️

Copy link
Author

Choose a reason for hiding this comment

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

굉장히 날카로운 지적이군요..
위와 같은 문제가 있을 가능성도 충분히 생각할만한 것 같습니다.

제가 Viewobject로 선언한 이유는 Controller 내부의 상태를 최대한 줄이기 위해서입니다.
뿐만 아니라 View 관련 객체를 싱글톤으로 사용하기 위함도 있습니다.
MVC 패턴에서 Model과 View는 서로 모르고 있어야 한다 라는 원칙은 직접적인 참조나 의존성을 가지지 않기만 한다면 만족한다고 생각합니다.
그렇기 때문에 코드를 작성하는 사람이 Model 내부에서 View 인스턴스를 직접 호출하지 않는 이상 문제가 생기지 않을 거라고 생각했습니다.

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.

3 participants