Skip to content

Conversation

@hyuunnn
Copy link

@hyuunnn hyuunnn commented Nov 19, 2022

확실히 다른 챌린지와 다르게 요구사항이 많아서 생각할게 많네요.

밤새면서 했더니 눈에 잘 안들어오네요 ㅋㅋ..

정신 멀쩡할 때 다시 확인해보고 지속적으로 업데이트하겠습니다 ㅎㅎ..

이번 문제는 테스트 코드가 필요하다는 생각이 들었습니다 ㅎㅎ..
얼른 만들어서 올리겠습니다..

@hyuunnn hyuunnn self-assigned this Nov 19, 2022
구매 금액의 단위가 1000으로 떨어지지 않는다고 가정하면 (ex: 8001) 구매한 로또 개수 * 1000의 값을 사용해야 한다.
로또 숫자 범위는 1~45이기 때문에 WinningNumber와 BonusNumber 둘 다 입력 받을 때 체크해야 한다.
효율적인 방법으로 수정
Comment on lines 22 to 45
private WinningLotto winningLotto;
private Lotto winningNumberList;
private int bonusNumber;
private int lottoCount;

public void run() {
input();
printResult();
}

private void input() {
System.out.println("구입 금액을 입력해주세요.");
while (inputPrice() == InputType.INVALID)
;

System.out.println("지난 주 당첨번호를 입력해 주세요.");
while (inputWinningNumber() == InputType.INVALID)
;

System.out.println("보너스 볼을 입력해 주세요.");
while (inputBonusNumber() == InputType.INVALID)
;

winningLotto = new WinningLotto(winningNumberList, bonusNumber);
Copy link
Author

@hyuunnn hyuunnn Nov 20, 2022

Choose a reason for hiding this comment

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

winningLottowinningNumberList, bonusNumber 값으로 인스턴스를 생성하는데,
class의 인스턴스 변수(winningNumberList, bonusNumber)와 동일한 값을 가지게 되어 비효율적이라고 생각합니다.

input() 메서드에서 해결하고 싶은데, 어떻게 하면 좋을지 의견을 얻어보고 싶습니다..!
winningNumberList가 필요하다면, winningLotto.getLotto()와 같은 방법을 사용하는 것이 더 깔끔하겠네요.

Copy link
Contributor

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.

포인터로 처리를 못하니까 어떻게 하면 좋을지 고민이 되네요 ㅋㅋ..
기본적인 구조가 잘못되었나 생각도 드네요...

@shkisme shkisme requested review from gusah009 and shkisme November 24, 2022 02:58
private void printRankMap(HashMap<Integer, Integer> rankMap) {
Arrays.stream(Rank.values())
.forEach(rankObj -> {
Optional<Integer> priceCount = Optional.ofNullable(rankMap.get(rankObj.getMatch()));
Copy link
Author

@hyuunnn hyuunnn Nov 24, 2022

Choose a reason for hiding this comment

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

Optional은 return으로 사용을 권고하고 있는데, 3항 연산자를 쓰는 것은 어떻게 생각하는지 궁금합니다!
https://johngrib.github.io/wiki/java/optional/

Copy link
Contributor

Choose a reason for hiding this comment

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

맞는 답변이 될 지는 모르겠지만, 올해 우테코 로또 게임 요구사항에서는 3항 연산자 사용을 금하고 있네요! 동일한 문제입니다.
https://github.com/woowacourse-precourse/java-lotto
아무래도 가독성? 측면에서 안좋다고 보아서 금하는 것 같아요

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

@gusah009 gusah009 left a comment

Choose a reason for hiding this comment

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

밤새서 만들었다니... ㄷㄷ 수고 많으셨습니다!!

GamePlayer 클래스가 너무 큰 느낌입니다. UI를 구성하는 출력 파트만 따로 분리하는 것도 좋을 것 같습니다!!

Comment on lines 11 to 15
public static void checkNumeric(String strNumber) {
if (isInteger(strNumber) == InputType.INVALID) {
throw new IllegalStateException(ErrorMessage.INVALID_NUMBER.get());
}
}

Choose a reason for hiding this comment

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

Error 클래스에 InputType이라는 클래스가 의존성이 생긴 것 같습니다.

에러와 입력을 담당하는 Input은 서로 의존성이 없어도 될 것 같다고 생각하는데, 어떻게 생각하시나요?

Copy link
Author

Choose a reason for hiding this comment

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

생각해보니 Input 처리 목적으로 사용하려고 했던 클래스인데 이상한 곳에서 쓰고 있네요.


import com.hyuunnn.lotto.Lotto;

public class BonusNumberError extends Error {

Choose a reason for hiding this comment

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

로또 자체를 검증하는 Error 클래스와 그를 상속받는 BonusNumberError로 사용하신 부분 좋은 것 같습니다!! 👍


import com.hyuunnn.lotto.Util.InputType;

public class Error {

Choose a reason for hiding this comment

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

Error라는 네이밍 대신 Validator라는 네이밍은 어떠신가요?

Copy link
Author

@hyuunnn hyuunnn Nov 25, 2022

Choose a reason for hiding this comment

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

에러보다 유효한지 검증하는 것이 주 목적이라서 더 좋겠네요!

} else if (matchCount == 6) {
return Rank.FIRST;
}
return null;

Choose a reason for hiding this comment

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

null을 반환할 수 있다면 Optional을 붙여주는 것도 좋을 것 같습니다!

Comment on lines 28 to 36
if (matchCount == 3) {
return Rank.FIFTH;
} else if (matchCount == 4) {
return Rank.FOURTH;
} else if (matchCount == 5) {
return checkBonusNumber(userLotto, bonusNumber);
} else if (matchCount == 6) {
return Rank.FIRST;
}

Choose a reason for hiding this comment

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

만약 "6등은 3개가 일치하고 bonusNumber가 일치하지 않아야 한다" 와 같은 요구사항이 추가되면 RankWinningLotto 둘 다 수정해야 합니다.

만약 이 코드를 전혀 다른 사람이 수정한다고 치면, Rank 요구사항이 바뀌었는데, WinningLotto를 수정해야 한다고 생각하기 쉽지 않고, 놓칠 수도 있습니다.

이를 OCP 위반이라고 하는데, 시간이 되신다면 한 번 OCP를 지키는 코드를 만들어 보는 것도 좋을 것 같습니다!

Copy link
Author

@hyuunnn hyuunnn Nov 25, 2022

Choose a reason for hiding this comment

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

디자인 패턴도 공부해서 반영해보겠습니다 ㅎㅎ

isNumeric 메서드는 일반적으로 많이 사용하는 방법이기 때문에 boolean으로 return 받는 방법으로 수정
해당 클래스의 기능들은 입력이 유효(validate)한지 검증한다.
이전에는 순위 조건이 추가될 때마다 if문이 늘어났는데, 이를 해결하는 코드를 작성하였다.
Bonus.NONE은 보너스 볼에 매치가 되거나, 그 반대여도 true를 반환한다는 것을 명시하기 위함
Comment on lines +44 to +47
if (this == Bonus.NONE) {
return true;
}
return match == bonusMatch;
Copy link
Author

Choose a reason for hiding this comment

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

return this == Bonus.NONE ? true : match == bonusMatch;

이런 부분은 삼항 연산자 쓰는게 더 깔끔해보이는데, 챌린지 규칙이 사용 금지라서 아쉽네요.

@hyuunnn
Copy link
Author

hyuunnn commented Dec 3, 2022

#1 (comment)
전에 언급했던 위 문제를 해결하려고 수정한 코드(acfb283) 입니다. (추가로 intellij에서 while (condition); 코드에 경고 메세지를 주네요.)

어떻게 처리하면 좋을지 괜찮은 아이디어가 떠오르지 않아서 이전에 사용했던 public enum InputType {VALID, INVALID}을 지우고 while (true)를 사용했는데 피드백을 받아보고 싶습니다..!

추가로 java hashmap 구현 코드에서 for ( ;; ) 형태의 무한루프를 사용하는데, is-for-faster-than-while-true-if-not-why-do-people-use-it 글을 보면 속도 차이는 없고, while (true)는 boolean 값에 의존하지만 for ( ;; )은 직관적이라는 내용도 있네요..

Copy link
Contributor

@shkisme shkisme left a comment

Choose a reason for hiding this comment

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

꼼꼼하게 너무 잘 짜셨네요 😲👍

public class GameInput {

public int inputPrice() {
while (true) {
Copy link
Contributor

Choose a reason for hiding this comment

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

말씀하신 것 처럼 for ( ;; ) 형태로 표현하면 true에 의존하지 않아서 개인적으로 더 좋은 것 같네요... 저는 처음 본 패턴인데, 배워갑니다! 👍👍

Copy link
Author

Choose a reason for hiding this comment

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

지인 찬스를 사용해봤는데, 대부분 while (true), while (1)을 그냥 사용한다고 하네요 ㅎㅎ..
제가 생각하기에는 init / condition / step이 존재하는 무한루프의 형태일 때 사용하는 것 같아요.

for (int binCount = 0; ; ++binCount) { ~~~ }
for (TreeNode<K, V> pointer_node = root; ; ) { ~~~ }

java hashmap 구현 코드의 일부입니다.

System.out.println("보너스 볼을 입력해 주세요.");
int bonusNumber = inputBonusNumber(winningNumberList);

winningLotto = new WinningLotto(new Lotto(winningNumberList), bonusNumber);
Copy link
Contributor

Choose a reason for hiding this comment

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

해결하셨군요 👍👍 계속 코드를 발전시키는 점 본받고 싶네요...ㅎㅎ
방치된 제 코드를 보니 반성하게 됩니다😓

}
}

enum ErrorMessage {
Copy link
Contributor

Choose a reason for hiding this comment

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

Validator 도 각각 분리하고 ErrorMessage도 그에 맞게 nest로 둔 점... 진짜👍

Copy link

@gusah009 gusah009 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 +32 to +35
enum Bonus {
MATCH(true),
MISMATCH(false),
NONE(true);
Copy link

Choose a reason for hiding this comment

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

Bonus enum 클래스를 활용해서 확장성있게 만드셨네요!! 좋습니다! 👍

Copy link
Author

@hyuunnn hyuunnn Dec 4, 2022

Choose a reason for hiding this comment

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

15979dd 에서 Rank 클래스의 isMatchBonus 클래스의 isMatch를 만들어서 연결하니까 직관적이네요 ㅎㅎ

Comment on lines 19 to 26
public static boolean isNotInteger(String str) {
try {
strToInteger(str);
} catch (NumberFormatException e) {
return true;
}
return false;
}
Copy link

Choose a reason for hiding this comment

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

혹시 UtilisNotIntegerValidator 클래스의 checkInteger의 차이를 알 수 있을까요?

사용성이 비슷한 것 같은데 서로 다른 두 메서드를 둔 이유가 궁금합니다!

Copy link
Author

Choose a reason for hiding this comment

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

실수입니다 ㅠㅠ.. 처음에 개발할 때는 단순히 숫자인지 체크하는 기능이라서 Util 클래스에 넣었는데, 지금 보니 검증할 때를 제외하면 사용되지 않네요.
Validator 클래스로 옮기겠습니다..!

}

lotto = new Lotto(testList);
assertThat(lotto.getLottoList().size()).isEqualTo(i);
Copy link

Choose a reason for hiding this comment

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

위 코드를 아래와 같이 바꿔도 좋을 것 같습니다!

assertThat(lotto.getLottoList()).hasSize(i);

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다 :)

Comment on lines +49 to +63
@Test
@DisplayName("WinningLotto match 메서드 테스트")
public void WinningLottoMatchTest() {
List<Integer> lottoList = Arrays.asList(1, 2, 3, 4, 5, 6);
assertRank(Arrays.asList(1, 2, 3, 4, 5, 6), lottoList, 7, Optional.of(Rank.FIRST));
assertRank(Arrays.asList(1, 2, 3, 4, 5, 7), lottoList, 7, Optional.of(Rank.SECOND));
assertRank(Arrays.asList(1, 2, 3, 4, 5, 8), lottoList, 7, Optional.of(Rank.THIRD));
assertRank(Arrays.asList(1, 2, 3, 4, 8, 9), lottoList, 7, Optional.of(Rank.FOURTH));
assertRank(Arrays.asList(1, 2, 3, 4, 8, 7), lottoList, 7, Optional.of(Rank.FOURTH));
assertRank(Arrays.asList(1, 2, 3, 8, 9, 10), lottoList, 7, Optional.of(Rank.FIFTH));
assertRank(Arrays.asList(1, 2, 3, 8, 9, 7), lottoList, 7, Optional.of(Rank.FIFTH));
assertRank(Arrays.asList(1, 2, 8, 9, 10, 11), lottoList, 7,Optional.empty());
assertRank(Arrays.asList(1, 8, 9, 10, 11, 12), lottoList, 7, Optional.empty());
assertRank(Arrays.asList(8, 9, 10, 11, 12, 13), lottoList, 7, Optional.empty());
}
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.

감사합니다! 참고해보겠습니다 ㅎㅎ

Validator에서 유일하게 사용되고 있으므로 가까운 위치로 옮겼다.
size()를 호출하여 비교하는 것이 아닌, assertThat에 구현되어 있는 hasSize를 사용
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.

4 participants