Skip to content
Open

240617 #2116

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package lotto;

public class Application {
public static void main(String[] args) {
public static void main(String[] args) throws MyException {

Choose a reason for hiding this comment

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

showMain 메소드에서는 예외를 던지지 않는데
메인에서 예외를 던지도록 되어 있네요
만약 메인에서 예외를 throws 한다면 어떻게 되나요?

// TODO: 프로그램 구현
Money money = new Money();
money.showMain();
}
}
20 changes: 17 additions & 3 deletions src/main/java/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
package lotto;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Lotto {
private final List<Integer> numbers;

public Lotto(List<Integer> numbers) {
public Lotto(List<Integer> numbers) throws MyException {
validate(numbers);
checkRange(numbers);
this.numbers = numbers;
}

private void validate(List<Integer> numbers) {
// 6자리인지 검사
private void validate(List<Integer> numbers) throws IllegalArgumentException {
if (numbers.size() != 6) {
throw new IllegalArgumentException();
throw new IllegalArgumentException("[ERROR] 로또 숫자는 6자리여야 합니다.");
}
}

// TODO: 추가 기능 구현

// 범위 확인
private void checkRange(List<Integer> numbers) throws MyException {
for (Integer number : numbers) {
if (number < 1 || number > 45) {
throw new MyException("[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
}
}
}
70 changes: 70 additions & 0 deletions src/main/java/lotto/Money.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package lotto;

import camp.nextstep.edu.missionutils.Randoms;
import camp.nextstep.edu.missionutils.Console;
import org.assertj.core.api.ThrowableAssert;

import java.util.*;

public class Money {
public void showMain() {
getMoney();
}

// 구입금액 입력받기
public ThrowableAssert.ThrowingCallable getMoney() {

Choose a reason for hiding this comment

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

예외를 반환하는데 예외의 타입이 assertj를 사용하시는데
자바 기본의 Throwable를 사용하는건 어떨까요?
그리고 null만 리턴하고 있어서 맞추는게 좋을 것 같아요 😢

try {
System.out.println("구입금액을 입력해주세요.");
int money = Integer.parseInt(Console.readLine());
getModulo(money);
showNum(money / 1000);
} catch (MyException e) { // 예외처리 반복 https://woojin.tistory.com/74 참고
System.out.println("[ERROR] 금액이 1000으로 나누어 떨어지지 않습니다.");
getMoney();
} catch (NumberFormatException e) { // 숫자가 아닐 경우도 예외처리
System.out.println("[ERROR] 숫자를 입력해주세요.");
getMoney();
}
return null;
}

// 1000으로 나누어 떨어지지 않으면 exception
public ThrowableAssert.ThrowingCallable getModulo(int money) throws MyException {

Choose a reason for hiding this comment

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

나누어 떨어지지 않으면 예외만 던지도록 하면 더 좋을 것 같아요
getModulo, showNum 메소드는 테스트 외에서 사용하지 않기 때문에
캡슐화를 하는게 어떨가 싶어요
테스트 때문에 public으로 변경하셨다면 어떻게 하면 좋을지 고민해보면 좋을 것 같아요

if (money % 1000 != 0) {
throw new MyException();
}
return null;
}

// 로또 번호 셀렉
public ThrowableAssert.ThrowingCallable showNum(int money) throws MyException {
System.out.println();

Choose a reason for hiding this comment

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

따로 출력되는 부분을 나누면 더 좋을 거 같아요~

System.out.println(money + "개를 구매했습니다.");
List<Integer>[] lotto = new List[money * 6];

Choose a reason for hiding this comment

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

배열은 가급적 사용하지 않는 걸 추천해요
다른 Collection 객체로 변경하게 되면 많은 리팩토링이 발생할 수 있어요

그리고 Lotto 객체를 사용했다면 좋았지 않을까 싶어요


for (int i = 0; i < money; i++) {
List<Integer> list = selectNum();
lotto[i] = list;
System.out.println(lotto[i]);
}

WinNum win = new WinNum();
win.showWin(lotto);
return null;
}

// 랜덤 숫자 고르고 Lotto로 넘김
private List<Integer> selectNum() throws MyException {
// https://qh5944.tistory.com/152 참고 -, UnsupportedOperationException 해결
List<Integer> numbers = Randoms.pickUniqueNumbersInRange(1, 45, 6);
List<Integer> result = new ArrayList<>();

Choose a reason for hiding this comment

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

List result = new ArrayList<>(Randoms.pickUniqueNumbersInRange(1, 45, 6)); 이렇게 하면 더 좋을거 같아요

result.addAll(numbers);
Collections.sort(result);
try {
new Lotto(result); // Lotto에서 6개, 범위, 중복까지 확인
} catch (MyException | IllegalArgumentException e) {
selectNum();
}
return result;
}
}
10 changes: 10 additions & 0 deletions src/main/java/lotto/MyException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package lotto;
// https://staticclass.tistory.com/72 참고
public class MyException extends java.lang.Exception {

Choose a reason for hiding this comment

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

커스텀 예외를 작성한 부분이 인상적이네요! 😃
Exception 패키지명 부분을 빼줬으면 더 좋았을 것 같아요

public MyException() {
}

public MyException(String errMoney) {
super(errMoney);
}
}
101 changes: 101 additions & 0 deletions src/main/java/lotto/Result.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package lotto;

import org.assertj.core.api.ThrowableAssert;

import java.util.*;

public class Result {
public void showResult(List<Integer>[] lotto, List<Integer> win, int bonus) {
HashMap<Integer, Integer> count = count(lotto, win, bonus);
HashMap<String, Integer> result = result(count);
double percent = rate(count, lotto.length / 6);
printResult(result, percent);
}

// 맞춘갯수끼리 map
private HashMap<Integer, Integer> count(List<Integer>[] lotto, List<Integer> win, int bonus) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < lotto.length / 6; i++) {
int correct = count(lotto[i], win);
if (correct == 5 && countBonus(lotto[i], bonus)) {
correct = 7; // 보너스까지 맞았다면 7로 설정
}
// mapping, https://gymdev.tistory.com/39 참고
map.put(correct, map.getOrDefault(correct, 0) + 1);
}
return map;
}

// 복권 한개당 일치하는 숫자 확인
public int count(List<Integer> lotto, List<Integer> win) {
int correct = 0;
for (int i = 0; i < lotto.size(); i++) {
if(countBonus(lotto, win.get(i))){
correct++;
}
}
return correct;
}

// 5개+보너스숫자 확인
private boolean countBonus(List<Integer> lotto, int bonus) {
for (Integer integer : lotto) {
if (integer == bonus) {

Choose a reason for hiding this comment

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

contains();

contains를 사용했다면 코드도 줄고 가독성도 더 높아질 것 같아요

return true;
}
}
return false;
}

// 순서대로 결과 mapping, https://tosuccess.tistory.com/138 참고
private HashMap<String, Integer> result(HashMap<Integer, Integer> count) {
LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
for (PRICE price : PRICE.values()) {
result.put(price.getRank(), 0);
if (count.containsKey(price.getNumber())) {
result.replace(price.getRank(), count.get(price.getNumber()));
}
}
return result;
}

// 수익률 구하기
private double rate(HashMap<Integer, Integer> result, int n) {

Choose a reason for hiding this comment

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

수익률의 금액도 같이 이넘에 넣으면 더 보기 좋을 거 같아요~

Choose a reason for hiding this comment

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

하드코딩은 좋지 않은 것 같아요 😭
enum을 좀 더 활용했으면 좋았을 것 같아요

double avg = (double) (result.getOrDefault(3, 0) * 5 + result.getOrDefault(4, 0) * 50
+ result.getOrDefault(5, 0) * 1500 + result.getOrDefault(7, 0) * 30000
+ result.getOrDefault(6, 0) * 2000000) / n * 100;

Choose a reason for hiding this comment

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

소수점 둘째 자리에서 반올림이 됐는지 확인이 어렵네요

return avg;
}

// 결과 출력
private void printResult(HashMap<String, Integer> result, double percent) {
for (String key : result.keySet()) {
System.out.println(key + result.get(key) + "개");
}
System.out.printf("총 수익률은 %3.1f%%입니다.", percent);
}
}

enum PRICE {

Choose a reason for hiding this comment

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

한 java 파일에 여러 클래스를 놔두기 보단 내부로 사용하거나
다른 파일로 빼는 게 좋을 것 같아요

FIFTH(3, "3개 일치 (5,000원) - "),
FOURTH(4, "4개 일치 (50,000원) - "),
THIRD(5, "5개 일치 (1,500,000원) - "),
SECOND(7, "5개 일치, 보너스 볼 일치 (30,000,000원) - "),
FIRST(6, "6개 일치 (2,000,000,000원) - ");

final private int number;
final private String rank;

public int getNumber() {
return number;
}

public String getRank() {
return rank;
}

private PRICE(int number, String text) {
this.number = number;
this.rank = text;
}
}
68 changes: 68 additions & 0 deletions src/main/java/lotto/WinNum.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package lotto;

import camp.nextstep.edu.missionutils.Console;

import java.util.ArrayList;
import java.util.Collections;
import java.util.InputMismatchException;
import java.util.List;

public class WinNum {
public void showWin(List<Integer>[] lotto) {
List<Integer> win = selectResult();
int bonus = getBonus();

Result result = new Result();
result.showResult(lotto, win, bonus);
}

// 당첨 숫자 입력받기
private List<Integer> selectResult() {
List<Integer> result = new ArrayList<>();
try {
System.out.println("\n당첨 번호를 입력해 주세요.");
String[] num = Console.readLine().split(",");
result = toList(num);
new Lotto(result);
} catch (IllegalArgumentException | MyException e) {
System.out.println(e.getMessage());
selectResult();
}
return result;
}

// 문자열 정수형으로 변환하며, Lotto에서 확인
private List<Integer> toList(String[] s) {
List<Integer> result = new ArrayList<>(s.length);
try {
for (String string : s) {
result.add(Integer.parseInt(string));
}
Collections.sort(result);
} catch (NumberFormatException e) {
System.out.println("[ERROR] 숫자가 아닙니다.");
selectResult();

Choose a reason for hiding this comment

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

순환 참조를 발생하고 toList 메소드를 실행하는 부분에 재귀를 해서 스택오버플로우 발생 위험이 있을 것 같아요
throw 예외를 발생하게 하는 건 어떨까요?

}
return result;
}

// 보너스 번호 입력받기
private int getBonus() {
try {
System.out.println("\n보너스 번호를 입력해 주세요.");
int num = Integer.parseInt(Console.readLine());
validate(num);
return num;
} catch (InputMismatchException | IllegalArgumentException e) {
System.out.println("[ERROR] " + e.getMessage());
getBonus();
}
return 0;
}

private void validate(int num) {
if (num < 1 || num > 45) {
throw new IllegalArgumentException("보너스 번호는 1~45 사이의 숫자여야 합니다.");
}
}
}
6 changes: 5 additions & 1 deletion src/test/java/lotto/ApplicationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class ApplicationTest extends NsTest {

@Override
public void runMain() {
Application.main(new String[]{});
try {
Application.main(new String[]{});
} catch (MyException e) {
throw new RuntimeException(e);
}
}
}
6 changes: 6 additions & 0 deletions src/test/java/lotto/LottoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,10 @@ void createLottoByDuplicatedNumber() {
}

// 아래에 추가 테스트 작성 가능
@DisplayName("로또 번호가 1부터 45 사이의 숫자가 아니라면 예외 발생한다.")
@Test
void checkRange() {
assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 46)))
.isInstanceOf(IllegalArgumentException.class);
}
}
26 changes: 26 additions & 0 deletions src/test/java/lotto/MoneyTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package lotto;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class MoneyTest {
@DisplayName("구입 금액 입력받기")
@Test
void nonNumericTest() throws MyException {

Choose a reason for hiding this comment

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

기능 구현한 하나의 메소드에서 많은 일을 처리하고 있어서 테스트하기 힘들었을 것 같아요
예외 처리에 대한 테스트도 좋지만 성공에 대한 케이스도 있으면 구현하는데 더 수월할 수 있어요

Money money = new Money();
assertThatThrownBy(money.getModulo(50))
.isInstanceOf(MyException.class);
}

@DisplayName("랜덤숫자 보여주기")
@Test
void showRandomNumber() throws MyException {
Money money = new Money();
assertThatThrownBy(money.showNum(8))
.isInstanceOf(MyException.class);
}
}