Skip to content
12 changes: 12 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
- 로또 구매 (구매 금액 입력)
PurchasedLotto: 구매 개수, 발행된 로또

- 로또 발행 (구매한 로또 출력)

- 당첨 로또 (당첨 번호 입력)
WinningLotto: 당첨 번호, 보너스 번호

- 당첨 통계 출력
WinningRank: 등수와 당첨금, enum 사용

- Exception 처리
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.controller.LottoController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
LottoController lottoController = new LottoController();
lottoController.run();
}
}
81 changes: 81 additions & 0 deletions src/main/java/lotto/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package lotto.controller;

import lotto.domain.Lotto;
import lotto.domain.PurchasedLotto;
import lotto.domain.WinningLotto;
import lotto.domain.WinningRank;
import lotto.view.InputView;
import lotto.view.OutputView;

import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class LottoController {
public void run() {
PurchasedLotto purchasedLotto = new PurchasedLotto(purchaseLotto());
publishLotto(purchasedLotto);
WinningLotto winningLotto = getLottoNumbers();
printLottoResult(purchasedLotto, winningLotto);
}

// 구매 금액 입력
private int purchaseLotto() {
return InputView.getPurchaseAmount();
}

// 구매한 로또 출력
private void publishLotto(PurchasedLotto purchasedLotto) {
OutputView.printLottoCount(purchasedLotto.getLottoCount());
OutputView.printPublishedLotto(purchasedLotto.getLottoSet());
}

// 당첨 번호와 보너스 번호 입력
private WinningLotto getLottoNumbers() {
WinningLotto winningLotto = new WinningLotto(InputView.getLottoNumbers());
winningLotto.setBonusNumber(InputView.getBonusNumber());
return winningLotto;
}

// 당첨 통계 출력
private void printLottoResult(PurchasedLotto purchasedLotto, WinningLotto winningLotto) {
Map<WinningRank, Integer> winningResult = calculateResult(purchasedLotto.getLottoSet(), winningLotto);
double returnRate = calculateReturnRate(purchasedLotto.getPurchaseAmount(), winningResult);
OutputView.printWinningResult(winningResult, returnRate);
}
Comment on lines +42 to +46
Copy link
Member

Choose a reason for hiding this comment

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

printLottoResult 과 printWinningResult 으로 메소드를 나눈 이유가 뭔가요??


// 당첨 통계 계산
private Map<WinningRank, Integer> calculateResult(List<Lotto> lottoSet, WinningLotto winningLotto) {
Map<WinningRank, Integer> winningResult = new EnumMap<>(WinningRank.class);
Arrays.stream(WinningRank.values()).forEach(winningRank -> winningResult.put(winningRank, 0));

for (Lotto lotto : lottoSet) {
int match = compareNumbers(lotto.getNumbers(), winningLotto.getWinningNumber());
boolean containBonus = isContainBonus(lotto.getNumbers(), winningLotto.getBonusNumber(), match);
WinningRank winningRank = WinningRank.findWinningRank(match, containBonus);
winningResult.replace(winningRank, winningResult.get(winningRank) + 1);
}
Comment on lines +53 to +58

Choose a reason for hiding this comment

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

이 부분도 계산하는 역할의 클래스를 분리하거나 메서드 분리가 좋아보입니다.


return winningResult;
}

// 로또 하나의 당첨 결과 계산
private int compareNumbers(List<Integer> lottoNumber, List<Integer> winningLotto) {
return (int) lottoNumber.stream().filter(winningLotto::contains).count();
}

// 보너스 번호 포함 여부 확인
private boolean isContainBonus(List<Integer> lottoNumber, int bonus, int match) {
if (match != 5) return false;
return lottoNumber.contains(bonus);
}
Comment on lines +68 to +72
Copy link
Member

Choose a reason for hiding this comment

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

Validation 클래스에 있어야할 메서드 같습니다


// 수익률 계산
private double calculateReturnRate(int purchaseAmount, Map<WinningRank, Integer> winningResult) {
long winningAmount = winningResult.entrySet().stream()
.mapToLong(entry -> (long) entry.getKey().getReword() * entry.getValue())
.sum();
return (double) winningAmount / purchaseAmount * 100.0f;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package lotto;
package lotto.domain;

import lotto.util.Validation;

import java.util.List;

Expand All @@ -7,6 +9,7 @@ public class Lotto {

public Lotto(List<Integer> numbers) {
validate(numbers);
Validation.validateDuplicateLottoNumber(numbers);
this.numbers = numbers;
}

Expand All @@ -16,5 +19,7 @@ private void validate(List<Integer> numbers) {
}
}

// TODO: 추가 기능 구현
public List<Integer> getNumbers() {
return numbers;
}
}
47 changes: 47 additions & 0 deletions src/main/java/lotto/domain/PurchasedLotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package lotto.domain;

import lotto.util.Validation;
import org.kokodak.Randoms;

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

// 구매한 로또
public class PurchasedLotto {
public static final int LOTTO_PRICE = 1000;

private final int lottoCount;
private final List<Lotto> lottoSet = new ArrayList<>();
Copy link
Member

Choose a reason for hiding this comment

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

List 는 Lottos 나 LottoList로 표현하는게 알맞는 명명법 같습니다.


public PurchasedLotto(int purchasedAmount) {
Validation.validateMoneyRange(purchasedAmount);
this.lottoCount = purchasedAmount / LOTTO_PRICE;
generatedLotto();
}

// 구매한 로또 생성
private void generatedLotto() {
for (int i=0; i<lottoCount; i++) {
List<Integer> numbers = new ArrayList<>(
Randoms.pickUniqueNumbersInRange(1, 45, 6));
Collections.sort(numbers);
lottoSet.add(new Lotto(numbers));
}
}
Comment on lines +24 to +31

Choose a reason for hiding this comment

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

역할 분리를 위해 메서드를 더 쪼개주시면 좋을 것 같습니다.


// 구매한 로또 개수 반환
public int getLottoCount() {
return lottoCount;
}

// 구매한 로또 반환
public List<Lotto> getLottoSet() {
return lottoSet;
}

// 구매 금액 반환
public int getPurchaseAmount() {
return lottoCount * LOTTO_PRICE;
}
}
32 changes: 32 additions & 0 deletions src/main/java/lotto/domain/WinningLotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package lotto.domain;

import lotto.util.Validation;

import java.util.List;

// 당첨 번호
public class WinningLotto {
private final Lotto winningNumber;
private int bonusNumber;

public WinningLotto(List<Integer> winningNumber) {
Validation.validateLotto(winningNumber);
this.winningNumber = new Lotto(winningNumber);
}

// 당첨 번호 반환
public List<Integer> getWinningNumber() {
return winningNumber.getNumbers();
}

// 보너스 번호 반환
public int getBonusNumber() {
return bonusNumber;
}

// 보너스 번호 설정
public void setBonusNumber(int bonusNumber) {
Validation.validateBonus(winningNumber.getNumbers(), bonusNumber);
this.bonusNumber = bonusNumber;
}
}
42 changes: 42 additions & 0 deletions src/main/java/lotto/domain/WinningRank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package lotto.domain;

import java.util.Arrays;

// 당첨 등수
public enum WinningRank {
LAST_RANK(0, 0, false),
FORTH_RANK(3, 5_000, false),
THIRD_RANK(4, 50_000, false),
SECOND_RANK(5, 1_500_000, false),
SECOND_BONUS_RANK(5, 30000000, true),
FIRST_RANK(6, 2_000_000_000, false);

private final int match;
private final int reword;
private final boolean bonus;

WinningRank(int match, int reword, boolean bonus) {
this.match = match;
this.reword = reword;
this.bonus = bonus;
}

// 맞힌 번호 개수 반환
public int getMatch() {
return match;
}

// 당첨금 반환
public int getReword() {
return reword;
}
Comment on lines +30 to +32
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 WinningRank findWinningRank(int match, boolean bonus) {
return Arrays.stream(values())
.filter(winningRank -> winningRank.match == match)
.filter(winningRank -> winningRank.bonus == bonus)
.findFirst()
.orElse(LAST_RANK);
}
}
88 changes: 88 additions & 0 deletions src/main/java/lotto/util/Validation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package lotto.util;

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

public class Validation {
private static final String INPUT_LOTTO_PRICE_ERROR = "[ERROR] 구입 금액은 1,000원 단위로 입력해주세요.";
private static final String INPUT_TYPE_ERROR = "[ERROR] 숫자를 입력해주세요.";
private static final String INPUT_NUMBER_RANGE_ERROR = "[ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.";
private static final String INPUT_LOTTO_SIZE_ERROR = "[ERROR] 당첨 번호는 서로 다른 6개의 수로 입력해주세요.";
private static final String LOTTO_NUMBER_DUPLICATED_ERROR = "[ERROR] 당첨 번호 중 중복되는 번호가 있습니다.";
private static final String INPUT_BONUS_RANGE_ERROR = "[ERROR] 보너스 번호는 1부터 45 사이의 숫자여야 합니다.";
private static final String LOTTO_BONUS_DUPLICATED_ERROR = "[ERROR] 당첨 번호 중 보너스 번호와 중복되는 번호가 존재합니다.";
Comment on lines +8 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.

ErrorCode를 따로 enum으로 나누는것이 깔끔할것같습니다.


private static final int ZERO = 0;
private static final int LOTTO_PRICE = 1_000;
private static final int LOTTO_MIN_NUMBER = 1;
private static final int LOTTO_MAX_NUMBER = 45;
private static final int LOTTO_NUMBER_SIZE = 6;

// 입력된 타입 확인
public static Integer parseInteger(String input) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(INPUT_TYPE_ERROR);
}
}

// 구매 금액은 0이상, 1000원 단위
public static void validateMoneyRange(int purchasedAmount) {
if (purchasedAmount <= ZERO || purchasedAmount % LOTTO_PRICE != 0) {
throw new IllegalArgumentException(INPUT_LOTTO_PRICE_ERROR);
}
}

// 로또 번호 적합성
public static void validateLotto(List<Integer> lotto) {
validateLottoNumber(lotto);
validateLottoNumberSize(lotto);
validateDuplicateLottoNumber(lotto);
}

// 로또 번호는 1~45
private static void validateLottoNumber(List<Integer> lotto) {
for (Integer l : lotto) {
if (l < LOTTO_MIN_NUMBER || l > LOTTO_MAX_NUMBER) {
throw new IllegalArgumentException(INPUT_NUMBER_RANGE_ERROR);
}
}
}

// 로또는 6개의 숫자
private static void validateLottoNumberSize(List<Integer> lotto) {
if (lotto.size() != LOTTO_NUMBER_SIZE) {
throw new IllegalArgumentException(INPUT_LOTTO_SIZE_ERROR);
}
}

// 로또 번호는 중복 없음
public static void validateDuplicateLottoNumber(List<Integer> lotto) {
Set<Integer> deduplicate = new HashSet<>(lotto);
if (deduplicate.size() != lotto.size()) {
throw new IllegalArgumentException(LOTTO_NUMBER_DUPLICATED_ERROR);
}
}

// 보너스 번호 적합성
public static void validateBonus(List<Integer> lotto, int bonus) {
validateBonusNumber(bonus);
validateDuplicateLottoBonus(lotto, bonus);
}

// 보너스 번호는 1~45
private static void validateBonusNumber(int bonus) {
if (bonus < LOTTO_MIN_NUMBER || bonus > LOTTO_MAX_NUMBER) {
throw new IllegalArgumentException(INPUT_BONUS_RANGE_ERROR);
}
}

// 보너스 번호는 당첨 번호와 중복 없음
private static void validateDuplicateLottoBonus(List<Integer> lotto, int bonus) {
if (lotto.contains(bonus)) {
throw new IllegalArgumentException(LOTTO_BONUS_DUPLICATED_ERROR);
}
}
}
35 changes: 35 additions & 0 deletions src/main/java/lotto/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package lotto.view;

import lotto.util.Validation;
import org.kokodak.Console;

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

public class InputView {
private static final String PURCHASE_AMOUNT_MESSAGE = "구입 금액을 입력해 주세요.";
private static final String WINNING_NUMBER_MESSAGE = "\n당첨 번호를 입력해 주세요.";
private static final String BONUS_NUMBER_MESSAGE = "\n보너스 번호를 입력해 주세요.";
private static final String SEPARATOR_VALUE = ",";

// 구매 금액 입력
public static int getPurchaseAmount() {
System.out.println(PURCHASE_AMOUNT_MESSAGE);
return Validation.parseInteger(Console.readLine());
}

// 당첨 번호 입력
public static List<Integer> getLottoNumbers() {
System.out.println(WINNING_NUMBER_MESSAGE);
return Arrays.stream(Console.readLine().split(SEPARATOR_VALUE))
.map(Validation::parseInteger)
.collect(Collectors.toList());
}

// 보너스 번호 입력
public static int getBonusNumber() {
System.out.println(BONUS_NUMBER_MESSAGE);
return Validation.parseInteger(Console.readLine());
}
}
Loading