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

[로또 게임 과제] 정건우 제출합니다 #17

Open
wants to merge 15 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
7 changes: 6 additions & 1 deletion src/main/java/lotto/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package lotto;

import lotto.controller.InstanceManager;
import lotto.view.LottoGame;

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

This file was deleted.

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

import lotto.model.LottoMachineModel;
import lotto.model.WinningResultModel;
import lotto.view.ConsoleInput;
import lotto.view.LottoGame;

public class InstanceManager {
Copy link
Collaborator

Choose a reason for hiding this comment

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

마치 스프링 컨테이너 역할을 하는 클래스를 만들었군요 좋습니다.

private final ConsoleInput consoleInput;
private final LottoMachineModel lottoMachine;
private final WinningResultModel winningResult;

public InstanceManager() {
this.consoleInput = new ConsoleInput();
this.lottoMachine = new LottoMachineModel();
this.winningResult = new WinningResultModel();
}

public LottoGame lottoGame() {
return new LottoGame(consoleInput, lottoMachine, winningResult);
}
}
21 changes: 21 additions & 0 deletions src/main/java/lotto/model/LottoMachineModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package lotto.model;

import camp.nextstep.edu.missionutils.Randoms;

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

public class LottoMachineModel {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Model은 데이터를 담는 객체를 말합니다. model 보다는 domain에 가까워 보입니다.

private static final int LOTTO_NUMBER_MIN = 1;
private static final int LOTTO_NUMBER_MAX = 45;
private static final int LOTTO_NUMBERS_COUNT = 6;

public List<LottoModel> generateLottos(int count) {
List<LottoModel> lottos = new ArrayList<>();
for (int i = 0; i < count; i++) {
List<Integer> numbers = Randoms.pickUniqueNumbersInRange(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_NUMBERS_COUNT);
lottos.add(new LottoModel(numbers));
}
return lottos;
}
}
36 changes: 36 additions & 0 deletions src/main/java/lotto/model/LottoModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package lotto.model;

// cannot reslove symbol -> gradle rebuild로 해결

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

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

public LottoModel(List<Integer> numbers) {
validate(numbers);
this.numbers = numbers;
}

private void validate(List<Integer> numbers) {
if (numbers.size() != 6) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
Set<Integer> numberSet = new HashSet<>();
for (int number : numbers) {
if (number < 1 || number > 45) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
if (!numberSet.add(number)) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다.");
}
}
}

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

public enum RankEnum {
FIRST(6, 2_000_000_000),
SECOND(5, 30_000_000),
THIRD(5, 1_500_000),
FOURTH(4, 50_000),
FIFTH(3, 5_000),
MISS(0, 0);

private final int matchCount;
private final int prize;

RankEnum(int matchCount, int prize) {
this.matchCount = matchCount;
this.prize = prize;
}

public int getMatchCount() {
return matchCount;
}

public int getPrize() {
return prize;
}

public static RankEnum valueOf(int matchCount, boolean matchBonus) {
if (matchCount == 6) {
return FIRST;
}
if (matchCount == 5 && matchBonus) {
return SECOND;
}
if (matchCount == 5) {
return THIRD;
}
if (matchCount == 4) {
return FOURTH;
}
if (matchCount == 3) {
return FIFTH;
}
return MISS;
}
}
46 changes: 46 additions & 0 deletions src/main/java/lotto/model/WinningResultModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package lotto.model;


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

public class WinningResultModel {
Copy link
Collaborator

Choose a reason for hiding this comment

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

model이 비즈니스 로직을 수행하네요. 물론 그러지 말라는 법은 없지만 mvc 패턴에서는 컨트롤러가 사용자로부터의 입력에 대한 응답으로 모델 및/또는 뷰를 업데이트하는 로직을 포함합니다.

private final Map<RankEnum, Integer> result = new EnumMap<>(RankEnum.class);

public WinningResultModel() {
for (RankEnum rank : RankEnum.values()) {
result.put(rank, 0);
}
}

public void checkWinning(List<LottoModel> purchasedLottos, Set<Integer> winningNumbers, int bonusNumber) {
for (LottoModel lotto : purchasedLottos) {
Set<Integer> intersection = new HashSet<>(lotto.getNumbers());
intersection.retainAll(winningNumbers);
int matchCount = intersection.size();
boolean matchBonus = lotto.getNumbers().contains(bonusNumber);

RankEnum rank = RankEnum.valueOf(matchCount, matchBonus);
result.put(rank, result.get(rank) + 1);
}
}

public void printWinningResult() {
System.out.println("당첨 통계\n---");
System.out.printf("3개 일치 (5,000원) - %d개%n", result.get(RankEnum.FIFTH));
System.out.printf("4개 일치 (50,000원) - %d개%n", result.get(RankEnum.FOURTH));
System.out.printf("5개 일치 (1,500,000원) - %d개%n", result.get(RankEnum.THIRD));
System.out.printf("5개 일치, 보너스 볼 일치 (30,000,000원) - %d개%n", result.get(RankEnum.SECOND));
System.out.printf("6개 일치 (2,000,000,000원) - %d개%n", result.get(RankEnum.FIRST));
}

public double calculateProfitRate(int purchaseAmount) {
int totalWinnings = result.entrySet().stream()
.mapToInt(entry -> entry.getKey().getPrize() * entry.getValue())
.sum();
return ((double) totalWinnings / purchaseAmount) * 100;
}
}
68 changes: 68 additions & 0 deletions src/main/java/lotto/view/ConsoleInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package lotto.view;

import camp.nextstep.edu.missionutils.Console;
import java.util.HashSet;
import java.util.Set;

public class ConsoleInput {
Copy link
Collaborator

Choose a reason for hiding this comment

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

입력에서 유효성 검증을 대부분 처리하네요. 물론 그러지 말라는 법은 없지만 유효성 검증도 비즈니스 로직에 포함되는 경우가 많습니다.

public int getPurchaseAmount() {
System.out.println("구입금액을 입력해 주세요.");
String input = Console.readLine();
int purchaseAmount;
try {
purchaseAmount = Integer.parseInt(input);
Copy link
Collaborator

Choose a reason for hiding this comment

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

예를 들면 정수가 아닌 입력은 잘못된 입력형식이라고 생각할 수 있어 입력단에서 처리하는게 나쁘지 않아보입니다.

} catch (NumberFormatException e) {
throw new IllegalArgumentException("구입 금액은 숫자여야 합니다.");
}
if (purchaseAmount % 1000 != 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

다만 구매 금액이 1000원단위가 아닌것은 잘못된 입력보다는 잘못된 "구매금액"아닐까요? 그렇다면 구매금액 객체를 관리하는 비즈니스 로직단에서 처리하는게 어떨까요?

throw new IllegalArgumentException("구입 금액은 1,000원 단위여야 합니다.");
}
return purchaseAmount;
}

public Set<Integer> getWinningNumbers() {
System.out.println("당첨 번호를 입력해 주세요.");
String input = Console.readLine();
String[] split = input.split(",");
if (split.length != 6) {
throw new IllegalArgumentException("당첨 번호는 6개여야 합니다.");
}

Set<Integer> winningNumbers = new HashSet<>();
for (String number : split) {
int num;
try {
num = Integer.parseInt(number);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("당첨 번호는 숫자여야 합니다.");
}
if (num < 1 || num > 45) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
winningNumbers.add(num);
}
if (winningNumbers.size() != 6) {
throw new IllegalArgumentException("당첨 번호는 중복되지 않는 6개의 숫자여야 합니다.");
}
return winningNumbers;
}

public int getBonusNumber(Set<Integer> winningNumbers) {
System.out.println("보너스 번호를 입력해 주세요.");
String input = Console.readLine();
int bonusNumber;
try {
bonusNumber = Integer.parseInt(input);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("보너스 번호는 숫자여야 합니다.");
}
if (bonusNumber < 1 || bonusNumber > 45) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
if (winningNumbers.contains(bonusNumber)) {
throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복되지 않아야 합니다.");
}
return bonusNumber;
}
}

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

import lotto.model.LottoModel;
import lotto.model.LottoMachineModel;
import lotto.model.WinningResultModel;

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

public class LottoGame {
Copy link
Collaborator

Choose a reason for hiding this comment

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

전반적인 흐름을 관리하는 것으로 보아 view보단 controller가 좀더 어울리네요


private static final int LOTTO_PRICE = 1000;
private final ConsoleInput consoleInput;
private final LottoMachineModel lottoMachine;
private final WinningResultModel winningResult;

public LottoGame(ConsoleInput consoleInput, LottoMachineModel lottoMachine, WinningResultModel winningResult) {
this.consoleInput = consoleInput;
this.lottoMachine = lottoMachine;
this.winningResult = winningResult;
}

public void start() {
try {
int purchaseAmount = getPurchaseAmount();
List<LottoModel> purchasedLottos = generateLottos(purchaseAmount);
printPurchasedLottos(purchasedLottos);

Set<Integer> winningNumbers = getWinningNumbers();
int bonusNumber = getBonusNumber(winningNumbers);

checkAndPrintWinningResult(purchasedLottos, winningNumbers, bonusNumber, purchaseAmount);
} catch (IllegalArgumentException e) {
System.out.println("[ERROR] " + e.getMessage());
}
}

private int getPurchaseAmount() {
return consoleInput.getPurchaseAmount();
}

private List<LottoModel> generateLottos(int purchaseAmount) {
int numberOfLottos = purchaseAmount / LOTTO_PRICE;
return lottoMachine.generateLottos(numberOfLottos);
}

private void printPurchasedLottos(List<LottoModel> lottos) {
System.out.printf("%d개를 구매했습니다.%n", lottos.size());
for (LottoModel lotto : lottos) {
System.out.println(lotto.getNumbers());
}
}

private Set<Integer> getWinningNumbers() {
return consoleInput.getWinningNumbers();
}

private int getBonusNumber(Set<Integer> winningNumbers) {
return consoleInput.getBonusNumber(winningNumbers);
}

private void checkAndPrintWinningResult(List<LottoModel> purchasedLottos, Set<Integer> winningNumbers, int bonusNumber, int purchaseAmount) {
winningResult.checkWinning(purchasedLottos, winningNumbers, bonusNumber);
winningResult.printWinningResult();

double profitRate = winningResult.calculateProfitRate(purchaseAmount);
System.out.printf("총 수익률은 %.1f%%입니다.%n", profitRate);
}
}
5 changes: 3 additions & 2 deletions src/test/java/lotto/LottoTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package lotto;

import lotto.model.LottoModel;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

Expand All @@ -11,15 +12,15 @@ class LottoTest {
@DisplayName("로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.")
@Test
void createLottoByOverSize() {
assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7)))
assertThatThrownBy(() -> new LottoModel(List.of(1, 2, 3, 4, 5, 6, 7)))
.isInstanceOf(IllegalArgumentException.class);
}

@DisplayName("로또 번호에 중복된 숫자가 있으면 예외가 발생한다.")
@Test
void createLottoByDuplicatedNumber() {
// TODO: 이 테스트가 통과할 수 있게 구현 코드 작성
assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5)))
assertThatThrownBy(() -> new LottoModel(List.of(1, 2, 3, 4, 5, 5)))
.isInstanceOf(IllegalArgumentException.class);
}

Expand Down