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

[로또 미션] 주병주 미션 제출합니다 #4

Open
wants to merge 1 commit 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
30 changes: 30 additions & 0 deletions src/main/java/Controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package Controller;

import Creator.RandomNumberCreator;
import Model.*;
import View.InputData;
import View.InputView;
import View.ResultView;

import java.util.ArrayList;

import static Model.Constants.LOTTO_PRICE;
import static View.InputData.*;
import static View.InputView.inputFirstLotto;

public class LottoController {
public static void main(String[] args) {
Lottos lottos = new Lottos();

Copy link

Choose a reason for hiding this comment

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

MVC 패턴에 기반하여 Controller, Model, View 나누어주신 것 같아요!
다만 main 함수가 있는 부분은 따로 빼주시는게 좋아보입니다! Controller가 LottoApplication의 main 함수 안에 존재하는 것이 맞을 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

좋은 생가인 것 같아요!

inputMoney = InputView.getInput();
handLotto = InputView.getHandLotto(lottos);
autoLotto = InputView.getAutoLotto(lottos,getAutoLottoCount(inputMoney, handLotto));
ResultView.printLottoResult(handLotto,autoLotto,lottos);
inputFirstLotto();
ResultView.finalResult(lottos);
}

private static int getAutoLottoCount(int inputMoney, int handLotto) {
return inputMoney / LOTTO_PRICE - handLotto;
}
}
5 changes: 5 additions & 0 deletions src/main/java/Creator/NumberCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package Creator;

public interface NumberCreator {
int returnNumber();
}
8 changes: 8 additions & 0 deletions src/main/java/Creator/RandomNumberCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package Creator;

import static java.lang.Math.random;
public class RandomNumberCreator implements NumberCreator{
public int returnNumber(){
return (int)(random()*45) + 1;
}
}
7 changes: 7 additions & 0 deletions src/main/java/Model/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package Model;

public class Constants {
public static final int MIN_PRICE = 0;
public static final int UNIT = 1000;
public static final int LOTTO_PRICE = 1000;
}
55 changes: 55 additions & 0 deletions src/main/java/Model/DataEnum.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package Model;

import View.InputData;

public class DataEnum {
public enum LottoResult {
THREE_MATCHES(3, false, 5000),
FOUR_MATCHES(4, false, 50000),
FIVE_MATCHES(5, false, 1500000),
FIVE_MATCHES_BONUS(5, true, 30000000),
SIX_MATCHES(6, false, 2000000000);

Copy link

Choose a reason for hiding this comment

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

Enum을 활용하여 당첨 상금 및 통계에 관하여 관리할 수 있도록 해주신 점 매우 좋은 것 같습니다

private final int matches;
private final boolean bonus;
private final int reward;

LottoResult(int matches, boolean bonus, int reward) {
this.matches = matches;
this.bonus = bonus;
this.reward = reward;
}

public int getMatches() {
return matches;
}

public boolean isBonus() {
return bonus;
}

public int getReward() {
return reward;
}

public static void applyResult(int[] matchList, int matchCount, boolean bonusResult) {
for (LottoResult result : values()) {
isRight(matchList, matchCount, bonusResult, result);
}
}

private static void isRight(int[] matchList, int matchCount, boolean bonusResult, LottoResult result) {
if (matchCount == 5 && result.bonus == bonusResult) {
int index = result.ordinal();
matchList[index]++;
InputData.profit += result.reward;
}
else if (result.matches == matchCount){
int index = result.ordinal();
matchList[index]++;
InputData.profit += result.reward;
}
}
}

}
33 changes: 33 additions & 0 deletions src/main/java/Model/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package Model;
import Creator.RandomNumberCreator;

import java.util.ArrayList;

public class Lotto {
private int price;
private LottoCreator lottoCreator;
private LottoValidater lottoValidater = new LottoValidater();
private ArrayList<LottoNumber> lottoNumbers = new ArrayList<>();

// 자동 로또
public Lotto(LottoPrice price, LottoCreator lottoCreator) {
this.price = price.getPrice();
this.lottoCreator = lottoCreator;
lottoNumbers = this.lottoCreator.getRandomNumbers(lottoNumbers,lottoValidater);
}

// 수동 로또
public Lotto(LottoPrice price,ArrayList<LottoNumber> lottoNumbers){
this.price = price.getPrice();
lottoValidater.validateLottos(lottoNumbers);
this.lottoNumbers = lottoNumbers;
}

public int getPrice() {
return price;
}

Copy link

Choose a reason for hiding this comment

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

Lotto 객체에서 getPrice() 메서드는 사용되지 않은 것 같아요. 마찬가지로 각 Lotto 객체가 price 필드를 가질 필요 또한 없어보입니다.
병주님께서 Constants 클래스로 이미" LOTTO_PRICE = 1000 "이라고 따로 관리해주시고 있기 때문에 중복되는 내용인 것 같아요!
price 필드를 지우게 되면 Lotto() 생성자의 매개변수 또한 줄어들면서 조금 더 가독성이 좋아지는 코드가 될 것 같습니다

Copy link
Author

Choose a reason for hiding this comment

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

좋은 지적인 것 같아요 :)

public ArrayList<LottoNumber> getLottoNumbers() {
return lottoNumbers;
}
}
35 changes: 35 additions & 0 deletions src/main/java/Model/LottoCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package Model;

import Creator.NumberCreator;
import Creator.RandomNumberCreator;

import java.util.ArrayList;


public class LottoCreator {
NumberCreator randomNumberCreator;

public LottoCreator(RandomNumberCreator randomNumberCreator) {
this.randomNumberCreator = randomNumberCreator;
}

public ArrayList<LottoNumber> getRandomNumbers(ArrayList<LottoNumber> lottoNumbers,LottoValidater lottoValidater) {
while (lottoNumbers.size() < 6) {
int returnNum = randomNumberCreator.returnNumber();

isNotInList(lottoNumbers, returnNum);
}
lottoValidater.validateLottos(lottoNumbers);
return lottoNumbers;
}

private static void isNotInList(ArrayList<LottoNumber> lottoNumbers, int returnNum) {
if (lottoNumbers.stream()
.map(LottoNumber::getNumber)
.noneMatch(num -> num == returnNum)) {
// `returnNum`과 일치하는 숫자가 없으면 true 반환
lottoNumbers.add(new LottoNumber(returnNum));
Copy link

Choose a reason for hiding this comment

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

isNotInList 메서드를 통해 로또 번호가 중복되는 번호 없게끔 확인하기 위해서 검증하는 부분을 넣어주신 점이 좋습니다.

다만 검증에서 통과하면 메서드 내부에서 로또 번호 랜덤으로 생성하여 추가하고 있는데 이 부분을 분리해주시면 좋을 것 같아요.
isNotInList 메서드에서는 이름 처럼 검증만 하여 boolean으로 검증에 통과했는지 실패했는지 반환해주고 그 외부에서 번호를 추가해주는 것이 좋을 것 같습니다

}
}

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

public class LottoNumber {
private int number;

public LottoNumber(int number) {
this.number = number;
}

public int getNumber() {
return number;
}
}
30 changes: 30 additions & 0 deletions src/main/java/Model/LottoPrice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package Model;

import static Model.Constants.MIN_PRICE;
import static Model.Constants.UNIT;

public class LottoPrice {
private int price;

public LottoPrice(int price) {
validate(price);
this.price = price;
}

public int getPrice() {
return price;
}

private void validate(int price) {
validateUnit(price);
validateMin(price);
}

private void validateMin(int price) {
if (price < MIN_PRICE) throw new IllegalArgumentException("값이 0원 미만일 수는 없습니다.");
}

private void validateUnit(int price) {
if (price % UNIT != 0) throw new IllegalArgumentException("가격은 1000원 단위이어야 합니다.");
}
}
22 changes: 22 additions & 0 deletions src/main/java/Model/LottoValidater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package Model;

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

public class LottoValidater {
public void validateLottos(ArrayList<LottoNumber> lottoNumbers) {
sizeCheck(lottoNumbers);
duplicationCheck(lottoNumbers);
}
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.

감사합니당!


public void duplicationCheck(ArrayList<LottoNumber> lottoNumbers) {
Set<Integer> seen = new HashSet<>();
if (lottoNumbers.stream()
.anyMatch(n -> !seen.add(n.getNumber()))) throw new IllegalArgumentException("로또는 중복돼서는 안됩니다.");
}

public void sizeCheck(ArrayList<LottoNumber> lottoNumbers) {
if (lottoNumbers.size() != 6) throw new IllegalArgumentException("로또의 개수는 6개가 돼야합니다");
}
}
33 changes: 33 additions & 0 deletions src/main/java/Model/Lottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package Model;

import View.InputData;

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

public class Lottos {
private ArrayList<Lotto> lottos = new ArrayList<>();

public void addNewLotto(Lotto lotto){
lottos.add(lotto);
}

public ArrayList<Lotto> getLottos() {
return lottos;
}

public int[] matchList(Lotto firstLotto, int bonus){
int[] matchList = new int[5];
List<Integer> answerLotto = firstLotto.getLottoNumbers().stream().map(LottoNumber::getNumber).toList();

for (Lotto lotto:lottos) {
List<Integer> buyLotto = lotto.getLottoNumbers().stream().map(LottoNumber::getNumber).toList();
int matchCount = (int) buyLotto.stream().filter(answerLotto::contains).count();
boolean bonusResult = buyLotto.contains(bonus);

DataEnum.LottoResult.applyResult(matchList,matchCount,bonusResult);
}
return matchList;
}

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

import Model.Lotto;

public class InputData {
private InputData() {
}
public static int inputMoney = 0;
public static int handLotto = 0;
public static int autoLotto = 0;
public static Lotto firstLotto;
public static int bonus;

public static int profit = 0;
}
61 changes: 61 additions & 0 deletions src/main/java/View/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package View;

import Creator.RandomNumberCreator;
import Model.*;

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

import static Model.Constants.LOTTO_PRICE;

public class InputView {
private static final Scanner sc = new Scanner(System.in);
public static int getInput() {
System.out.println("구입금액을 입력해 주세요.");
return sc.nextInt();
}
public static int getHandLotto(Lottos lottos){
System.out.println("수동으로 구매할 로또 수를 입력해 주세요.");
int numHand = sc.nextInt();
sc.nextLine();
handLotto(lottos, numHand);

Copy link

Choose a reason for hiding this comment

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

getHandLotto()는 InputView객체의 목적에 맞게 사용자로부터 구매할 로또 수만을 입력 받도록 하는게 좋을 것 같아요.
getHandLotto()가 호출한 handLotto() 메서드에서는 수동으로 구매할 번호를 입력받아 즉시 Lotto 객체를 생성하여 lottos에 추가하고 있는데 이 부분은 모두 분리될 필요가 있어보입니다.

getHandLotto() : 사용자로부터 구매할 로또 수 입력
handLotto() : 수동 번호 입력
아래는 가상의 메서드입니다.
makeHandLotto() : 입력 받은 수동 번호로부터 Lotto 객체 생성

위와 같이 메서드의 기능을 분리하여 각 메서드가 하나의 역할만을 수행하도록 하고 이 메서드들을 Controller가 활용하도록 하는 것이 객체 지향 프로그래밍에 더욱 알맞을 것 같아요 !

Copy link
Author

Choose a reason for hiding this comment

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

좋은 의견 감사합니다!

return numHand;
}

private static void handLotto(Lottos lottos, int numHand) {
if (numHand > 0) System.out.println("수동으로 구매할 번호를 입력해 주세요.");
for (int i = 0; i < numHand; i++) {
ArrayList<LottoNumber> numbers = Arrays.stream(sc.nextLine().split(", "))
.map(x -> new LottoNumber(Integer.parseInt(x)))
.collect(Collectors.toCollection(ArrayList::new));

Copy link

Choose a reason for hiding this comment

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

로또 번호 하나를 LottoNumber 객체를 통해서 원시값을 포장하려고 해주신 것 같아요.
LottoNumber 클래스가 검증 및 예외 처리 ( 1~45 이내 숫자만 포함하는지 ) 기능을 갖고 있다면 더 좋을 것 같습니다!

https://velog.io/@kanamycine/Java-%EC%9B%90%EC%8B%9C%EA%B0%92-%ED%8F%AC%EC%9E%A5

위 블로그에 같은 로또 번호 검증 및 예외 처리가 있더라구요 참조해보셔도 좋을 것 같아요

lottos.addNewLotto(new Lotto(new LottoPrice(LOTTO_PRICE),numbers));
}
}

public static int getAutoLotto(Lottos lottos,int total){
for (int i = 0; i < total; i++) {
Lotto lotto = new Lotto(new LottoPrice(1000), new LottoCreator(new RandomNumberCreator()));
ArrayList<LottoNumber> lottoNumbers = lotto.getLottoNumbers();
lottos.addNewLotto(lotto);
}
return total;
}

public static void inputFirstLotto(){
System.out.println();
System.out.println("지난 주 당첨 번호를 입력해 주세요.");
ArrayList<LottoNumber> numbers = Arrays.stream(sc.nextLine().split(", "))
.map(x -> new LottoNumber(Integer.parseInt(x)))
.collect(Collectors.toCollection(ArrayList::new));
InputData.firstLotto = new Lotto(new LottoPrice(LOTTO_PRICE),numbers);
System.out.println();
System.out.println("보너스 볼을 입력해 주세요.");
InputData.bonus = sc.nextInt();
}
}

31 changes: 31 additions & 0 deletions src/main/java/View/ResultView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package View;
import Model.*;

import static View.InputData.bonus;
import static View.InputData.firstLotto;

public class ResultView {
public static void printLottoResult(int hand, int auto, Lottos lottos) {
System.out.println("수동으로 " + hand + "장, 자동으로 " + auto + "개를 구매했습니다.");
lottos.getLottos()
.forEach(x -> System.out.println(x.getLottoNumbers()
.stream()
.map(LottoNumber::getNumber)
.sorted()
.toList()));

}
public static void finalResult(Lottos lottos) {
printResults(lottos.matchList(firstLotto, bonus));
}

public static void printResults(int[] matchCounts) {
for (DataEnum.LottoResult rank : DataEnum.LottoResult.values()) {
int count = matchCounts[rank.ordinal()];
System.out.printf("%d개 일치 (%d원)- %d개\n", rank.getMatches(), rank.getReward(), count);
}
double result = (double)InputData.profit / InputData.inputMoney;
System.out.printf("총 수익률은 %.2f입니다.\n",Math.floor(result * 100) / 100);
}

}
Loading