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

[로또] 조윤호 제출합니다 #3

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 4 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package lotto;

import lotto.lotto.LottoService;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
LottoService lottoService = new LottoService();
lottoService.run();
}
}
20 changes: 0 additions & 20 deletions src/main/java/lotto/Lotto.java

This file was deleted.

12 changes: 12 additions & 0 deletions src/main/java/lotto/config/LottoConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lotto.config;

public class LottoConfig {

private LottoConfig() {
}

public final static int LOTTO_NUM_LENGTH = 6;
public final static int LOTTO_MIN_NUM = 1;
public final static int LOTTO_MAX_NUM = 45;
Comment on lines +8 to +10
Copy link
Member

Choose a reason for hiding this comment

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

public static final 형태로 상수를 관리하는 것은 굉장히 안좋은 방법입니다!
절차 지향적인 방법으로 코드를 작성하고 계신것 같아요


}
65 changes: 65 additions & 0 deletions src/main/java/lotto/lotto/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package lotto.lotto;

import lotto.config.LottoConfig;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

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

public Lotto(List<Integer> numbers) {
validate(numbers);

try {
numbers.sort(Comparator.naturalOrder());
} catch (UnsupportedOperationException ignored) {}
Comment on lines +17 to +19
Copy link
Member

Choose a reason for hiding this comment

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

프로덕션 코드에서 테스트코드의 영향을 받아야할까요?
테스트 작성시 문제가 생겼다면 테스트 코드를 변경하는게 맞지 않을까요??


this.numbers = numbers;
}

private void validate(List<Integer> numbers) {
if (numbers.size() != LottoConfig.LOTTO_NUM_LENGTH)
throw new IllegalArgumentException("[ERROR] 숫자의 개수가 올바르지 않습니다.");
if (numbers.size() != (new HashSet<>(numbers)).size())
throw new IllegalArgumentException("[ERROR] 숫자에 중복이 없어야 합니다.");
Comment on lines +25 to +28

Choose a reason for hiding this comment

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

validate가 한 번에 '개수 검증'과 '중복 검증'이라는 두 가지 일을 처리하고 있네요.
개수 검증과 중복 검증을 분리하면 메소드의 책임이 좀 더 명확해질 것 같아요!

}


public Prize comparePrize(Lotto winningLotto, int bonus) {
List<Integer> list = new ArrayList<>();
list.addAll(numbers);
list.addAll(winningLotto.numbers);
int matches = list.size() - (new HashSet<>(list)).size();
boolean isBonus = numbers.contains(bonus);

Prize prize = Prize.NONE;
for (Prize p : Prize.values()) {
if (
p.isPrized() &&
p.getMatchNum() <= matches &&
p.getGrade() <= prize.getGrade()
) {
if (p.isBonus() && !isBonus) // 보너스 true인 경우 isBonus 확인한다.
continue;
prize = p;
}
}
Comment on lines +40 to +50

Choose a reason for hiding this comment

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

  • indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
    • 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
    • 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
      라는 프로그래밍 요구사항을 만족하지 않는 구현 같습니다!

return prize;
}

public boolean contains(int num) {
return numbers.contains(num);
}

@Override
public String toString() {
return "[" +
numbers.stream().map(String::valueOf).collect(Collectors.joining(", ")) +
"]";
}

}
99 changes: 99 additions & 0 deletions src/main/java/lotto/lotto/LottoBundle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package lotto.lotto;

import lotto.purchase.PurchaseService;
import lotto.util.LottoUtil;
import lotto.util.UserOutput;

import java.util.ArrayList;
import java.util.List;

public class LottoBundle {
private List<Lotto> lottos = new ArrayList<>();
private Lotto winningLotto;
private int bonus;

Comment on lines +10 to +14
Copy link
Member

Choose a reason for hiding this comment

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

불변 객체로 만들지 않으면 프로그램이 동작할때 어떤 문제가 발생할까요?


/**
* lottoNum의 수만큼 새로운 로또 생성하기
*/
public void createLottos(int lottoNum) {
for (int i = 0; i < lottoNum; i++) {
Lotto lotto = new Lotto(LottoUtil.createLotto());
lottos.add(lotto);
}
}

/**
* 당첨번호 저장하기
*/
public void setWinningLotto(List<Integer> winningNumList) {
Lotto lotto = new Lotto(winningNumList);
this.winningLotto = lotto;
Comment on lines +30 to +31

Choose a reason for hiding this comment

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

Suggested change
Lotto lotto = new Lotto(winningNumList);
this.winningLotto = lotto;
this.winningLotto = new Lotto(winningNumList);

재사용 가능성이 없는 경우 변수에 담지 않고 바로 사용하는게 더 좋은 것 같아요!

}

/**
* 보너스번호 저장하기
*/
public void setBonus(int bonus) {

Choose a reason for hiding this comment

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

setter 사용 대신 좀 더 의미있는 네이밍을 하면 좋을 것 같아요!

if (winningLotto.contains(bonus)) {
throw new IllegalArgumentException("[ERROR] 보너스 번호와 당첨 번호에 중복이 있습니다.");
}
this.bonus = bonus;
}

/**
* 로또 출력하기
*/
public void printLottos() {
StringBuilder sb = new StringBuilder();

for (Lotto lotto : lottos)
sb.append(lotto).append("\n");

UserOutput.printLottos(lottos.size(), sb.toString());
Copy link
Member

Choose a reason for hiding this comment

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

Domain에서 view에 의존하는 것이 맞을까요?
MVC 패턴을 생각해보면 Model View Controller 가 서로 분리되도록 설계합니다.
왜 분리되어야할까요?

}

/**
* 당첨 통계 출력하기
*/
public void printResult() {
int[] prizeStat = getPrizeStat();
int totalreward = getTotalReward(prizeStat);
double rewardPercent = PurchaseService.getRewardPercent(lottos.size(), totalreward);

StringBuilder sb = new StringBuilder();
for (Prize prize : Prize.values()){
if (!prize.isPrized()) continue;
sb.append(prize.getToString());
sb.append(
String.format(" (%,d원) - %d개",
prize.getReward(),
prizeStat[prize.ordinal()]
));
sb.append("\n");
}
sb.append(String.format("총 수익률은 %.1f%%입니다.", rewardPercent));

UserOutput.printResult(sb.toString());
}

public int[] getPrizeStat() {
int[] prizeStat = new int[Prize.values().length];
for (Lotto lotto : lottos) {
Prize prize = lotto.comparePrize(winningLotto, bonus);
if (prize.isPrized())
prizeStat[prize.ordinal()]++;
}
return prizeStat;
}

public int getTotalReward(int[] prizeStat) {
Comment on lines +80 to +90

Choose a reason for hiding this comment

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

두 메소드는 LottoBundle 내부에서만 사용되네요. private로 선언해주시면 좋을 것 같아요!

int totalReward = 0;
for (Prize prize : Prize.values()){
if (prizeStat[prize.ordinal()] != 0)
totalReward += prize.getReward() * prizeStat[prize.ordinal()];
}
return totalReward;
}

}
74 changes: 74 additions & 0 deletions src/main/java/lotto/lotto/LottoService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package lotto.lotto;

import lotto.purchase.PurchaseService;
import lotto.util.UserInput;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class LottoService {

public void run() {
LottoBundle bundle = new LottoBundle();

// 구입금액 입력받기
String userInput = UserInput.getCostInput();
int cost = getNum(userInput);
int lottoNum = PurchaseService.getLottoNum(cost);
Comment on lines +18 to +19

Choose a reason for hiding this comment

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

구입금액은 그냥 정수보다 좀 더 의미있는 값 같아요. '구입금액'이라는 객체를 생성하는 건 어떨까요?
그렇게 되면 '구입금액'이라는 객체를 생성할 때 검증을 할 수 있어서, 구입금액을 구매할 로또 개수를 가져올 때 계산하는 것보다 흐름이 자연스러울 것 같아요!

bundle.createLottos(lottoNum);

// 로또 출력하기
bundle.printLottos();

// 당첨번호 입력받기
userInput = UserInput.getLottoInput();
List<Integer> winningList = getNumList(userInput);
bundle.setWinningLotto(winningList);

// 보너스번호 입력받기
userInput = UserInput.getBonusInput();
int bonus = getNum(userInput);
bundle.setBonus(bonus);

// 당첨 통계 출력하기
bundle.printResult();
}


public int getNum (String input) {
int num;
try {
num = Integer.parseInt(input);
} catch (NumberFormatException ne) {
throw new IllegalArgumentException("[ERROR] 정수를 입력해주세요.");
}
return num;
Comment on lines +41 to +47

Choose a reason for hiding this comment

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

이런 식으로 early return 패턴을 사용하는게 더 가독성이 좋을 것 같아요!

Suggested change
int num;
try {
num = Integer.parseInt(input);
} catch (NumberFormatException ne) {
throw new IllegalArgumentException("[ERROR] 정수를 입력해주세요.");
}
return num;
try {
return Integer.parseInt(input);
} catch (NumberFormatException ne) {
throw new IllegalArgumentException("[ERROR] 정수를 입력해주세요.");
}

}

public List<Integer> getNumList(String input) {
List<String> strList = new ArrayList<>(
Arrays.asList(
input.split(",")
)
);

// "," 개수를 이용해서 배열의 길이가 올바르게 입력되었는지 확인한다.
int commas = input.length() - input.replace(String.valueOf(","), "").length();
if (commas+1 != strList.size())
throw new IllegalArgumentException("[ERROR] 정수와 ,로 이루어진 배열을 입력해주세요.");

List<Integer> numList = strList.stream()
.map(s -> {
try {
return Integer.parseInt(s);
} catch (NumberFormatException ne) {
throw new IllegalArgumentException("[ERROR] 정수와 ,로 이루어진 배열을 입력해주세요.");
}
} ).collect(Collectors.toList());

return numList;
}

}
51 changes: 51 additions & 0 deletions src/main/java/lotto/lotto/Prize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package lotto.lotto;

public enum Prize {
FIFTH(true, 5, 3, false, 5000, "3개 일치"),
FOURTH(true, 4, 4, false, 50000, "4개 일치"),
THIRD(true, 3, 5, false, 1500000, "5개 일치"),
SECOND(true, 2, 5, true, 30000000, "5개 일치, 보너스 볼 일치"),
FIRST(true, 1, 6, false, 2000000000, "6개 일치"),
NONE(false, 99999, 0, false, 0, "");

private final boolean prized;
private final int grade;
private final int matchNum;
private final boolean bonus;
private final int reward;
private final String toString;

Prize(boolean prized, int grade, int matchNum, boolean bonus, int reward, String toString) {
this.prized = prized;
this.grade = grade;
this.matchNum = matchNum;
this.bonus = bonus;
this.reward = reward;
this.toString = toString;
}

public boolean isPrized() {
return prized;
}

public int getGrade() {
return grade;
}

public int getMatchNum() {
return matchNum;
}

public boolean isBonus() {
return bonus;
}

public int getReward() {
return reward;
}

public String getToString() {
return toString;
}

}
30 changes: 30 additions & 0 deletions src/main/java/lotto/purchase/PurchaseService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package lotto.purchase;

public class PurchaseService {

private final static int PRICE_PER_LOTTO = 1000;

private PurchaseService() {
}

/**
* 텍스트 입력을 lotto 수로 변환
*/
public static int getLottoNum(int cost) {
if (cost <=0 )
throw new IllegalArgumentException("[ERROR] 1 이상의 양수를 입력해주세요");
Comment on lines +14 to +15

Choose a reason for hiding this comment

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

음수와 0 처리는 생각하지 못했던 부분이네요! 👍🏻

if (cost % PRICE_PER_LOTTO != 0)
throw new IllegalArgumentException("[ERROR] 가격의 배수에 해당하는 가격을 입력해주세요");
Comment on lines +14 to +17

Choose a reason for hiding this comment

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

검증의 책임을 분리하면 좋을 것 같아요!


return cost / PRICE_PER_LOTTO;
}

/**
* 총 로또 수익률을 퍼센트 단위로 반환
*/
public static double getRewardPercent(int num, int reward){
return (double) reward / (num * PRICE_PER_LOTTO) * 100;
}


}
21 changes: 21 additions & 0 deletions src/main/java/lotto/util/LottoUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package lotto.util;

import camp.nextstep.edu.missionutils.Randoms;
import lotto.config.LottoConfig;

import java.util.List;

public class LottoUtil {

private LottoUtil() {
}

public static List<Integer> createLotto() {
return Randoms.pickUniqueNumbersInRange(
LottoConfig.LOTTO_MIN_NUM,
LottoConfig.LOTTO_MAX_NUM,
LottoConfig.LOTTO_NUM_LENGTH
);
}

}
Loading