Skip to content
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
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# java-lotto

로또 미션 저장소

# [미션 리드미](https://github.com/talmood/private-mission-README/tree/main/%EB%AF%B8%EC%85%98%203%20-%20%EB%A1%9C%EB%98%90)


# 기능 목록 정리

## 입력
* 값을 입력받을 수 있도록 한다.
* 구매 금액, 당첨 번호, 보너스 볼

## 출력
* 값을 출력하도록 한다.

## 로또 생성
* 구매 금액을 받아서 몇 개의 로또를 생성할지 계산한다.
* 로또 번호를 생성한다.
* 1부터 45중 6개로 구성

## 당첨 조회
* 당첨 여부를 조회한다.


## 당첨 통계
* 일치하는 번호의 수 별로 통계를 구한다.
* 3개~6개까지 일치하는 경우, 5개 + 보너스 볼 일치
* 수익률 계산
* 3개 일치 5000원
* 4개 일치 5000원
* 5개 일치 150000원
* 5개+보너스볼 30000000원
* 6개 일치 2000000000원
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ repositories {
dependencies {
testImplementation 'org.assertj:assertj-core:3.22.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
// Mockito Dependency
testImplementation 'org.mockito:mockito-core:3.9.0'

// Mockito JUnit 5 Integration
testImplementation 'org.mockito:mockito-junit-jupiter:3.9.0'
}

java {
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public class Application {
public static void main(String[] args) {
LottoProcessor lottoProcessor = new LottoProcessor(new InputView(), new ResultView());
lottoProcessor.processGame();
}
}
7 changes: 7 additions & 0 deletions src/main/java/GameCountDecider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class GameCountDecider {

static final Integer LOTTO_PRICE = 1000;
Copy link
Member

Choose a reason for hiding this comment

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

LOTTO_PRICE 상수를 같은 패키지에서 사용하지 않을거라면 private 접근제어자로 정의해주는 게 좋을 것 같은데 어떻게 생각하시나요?

public Integer calculateGameForPrice(Integer price) {
return price/LOTTO_PRICE;
}
}
31 changes: 31 additions & 0 deletions src/main/java/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import java.util.Scanner;

public class InputView {
Scanner scanner = new Scanner(System.in);

public void guideToPutPurchasePrice() {
System.out.println("구매금액을 입력해주세요");
}
public void guideToPutWinningNumbers() {
System.out.println("지난 주 당첨 번호를 입력해 주세요.");
}

public void guideToPutBonusNumber() {
System.out.println("보너스 볼을 입력해 주세요.");
}

public String acceptInput() {
return scanner.nextLine();
}

public Integer validatePurchasePrice(String input) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException e) {
System.out.println("숫자를 입력해주세요");
return null;
}
}
Comment on lines +21 to +28
Copy link
Member

Choose a reason for hiding this comment

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

요 메서드는 네이밍만 보면 구매 금액에 대해 validate를 해준다는 의미인 것 같은데 실제로는 null을 반환하고 있고 해당 메서드를 호출하는 외부에서 null에 대해서 do while 문으로 처리를 해주고 있어요~

validate를 하고자 한다면 유효성 검사 후 Exception을 발생시켜 외부에서는 해당 Exception에 대해 처리하도록 하는 건 어떨까요?

추가로 validate 역할을 수행하는 Validator 객체를 만들어 해당 객체에서 처리하도록 하는 것도 고려해보셨으면 해요😄



}
40 changes: 40 additions & 0 deletions src/main/java/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Lotto {

public void setLottoGames(List<LottoGame> lottoGames) {
LottoGames = lottoGames;
}

List<LottoGame> LottoGames;

public List<LottoGame> getLottoGames() {
return new ArrayList<>(LottoGames);
}

public Lotto(Integer gameCount) {
this.LottoGames = new ArrayList<>();
for (int i = 0; i < gameCount; i++) {
LottoGame lottoGame = createLottoGame();
this.LottoGames.add(lottoGame);
}
}

private LottoGame createLottoGame() {
int minimumLottoValue = 1;
int maximumLottoValue = 46;
int lottoNumberCount = 6;
Comment on lines +26 to +28
Copy link
Member

Choose a reason for hiding this comment

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

해당 값들은 Lotto 라는 도메인에서 종속된 값이기 때문에 Lotto 객체에서 상수로 가지고 있어도 무방할 값으로 보여요!

List<Integer> potentialLottoNumbers = new ArrayList<>();
for (int i = minimumLottoValue; i <= maximumLottoValue; i++) {
potentialLottoNumbers.add(i);
}
Collections.shuffle(potentialLottoNumbers);
List<Integer> gameNumbers = new ArrayList<>(potentialLottoNumbers.subList(0, lottoNumberCount));
Collections.sort(gameNumbers);

return new LottoGame(gameNumbers);
}

}
20 changes: 20 additions & 0 deletions src/main/java/LottoGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import java.util.ArrayList;
import java.util.List;

public class LottoGame {
private final List<Integer> gameNumbers;

public LottoGame(List<Integer> gameNumbers) {
this.gameNumbers = new ArrayList<>(gameNumbers);
}

@Override
public String toString() {
return gameNumbers.toString();
}
Comment on lines +11 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.

혹시 해당 toString() 메서드는 로깅 용도로 사용하기 위해 오버라이딩 하셨나요? 오버라이딩 하신 이유가 궁금해요!


public List<Integer> getGameNumbers() {
return gameNumbers;
}

}
76 changes: 76 additions & 0 deletions src/main/java/LottoProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class LottoProcessor {
Copy link
Member

Choose a reason for hiding this comment

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

LottoProcessor 객체에서 정의해주고 있는 public 메서드가 5개로 은근 많은 역할을 수행하고 있어보여요~!
각 역할 별로 객체를 나눠보는 것은 어떨까요?


private final InputView inputView;

private final ResultView resultView;

public LottoProcessor(InputView inputView, ResultView resultView) {
this.inputView = inputView;
this.resultView = resultView;
}

public void processGame() {

Integer price = acceptPriceForLottoGame();
Integer gameCount = purchaseGamesByPrice(price);
Comment on lines +18 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.

Integer를 사용하고 있어 최종적으로 printPurchasedGameCount() 메서드에서 결과값을 보여줄 때 null 값이 출력될 수도 있을 것 같아요!
null 에 대한 상태가 필요해서 Integer 타입으로 하신 건지 궁금해요!

Lotto lotto = createLottoByGameCount(gameCount);
WinningLottoNumber winningLottoNumber = acceptWinningNumbers();
Winners winners = selectWinners(lotto, winningLottoNumber);
showWinnerStatistics(winners, price);
}

private void showWinnerStatistics(Winners winners, Integer price) {
resultView.printWinnerStatisticsMessage();
ProfitCalculator profitCalculator = new ProfitCalculator(winners, price);
resultView.printWinnerStatistics(winners, profitCalculator);
}


public Lotto createLottoByGameCount(Integer gameCount) {
Lotto lotto = new Lotto(gameCount);
resultView.printLottoGameNumbers(lotto);
return lotto;
}

public Integer purchaseGamesByPrice(Integer price) {
GameCountDecider gameCountDecider = new GameCountDecider();
Integer gameCount = gameCountDecider.calculateGameForPrice(price);
resultView.printPurchasedGameCount(gameCount);

return gameCount;
}

private int acceptPriceForLottoGame() {
inputView.guideToPutPurchasePrice();
Integer price;

do {
String stringInput = inputView.acceptInput();
price = inputView.validatePurchasePrice(stringInput);
} while (price == null);

return price;
}

private WinningLottoNumber acceptWinningNumbers() {
inputView.guideToPutWinningNumbers();
String stringWinningNumbers = inputView.acceptInput();
List<Integer> winningNumbers = Arrays.stream(stringWinningNumbers.split(","))
.map(String::trim)
Copy link
Member

Choose a reason for hiding this comment

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

String.trim() 메서드는 문자열의 양 끝단의 공백을 지워주는 메서드인데, 문자열 내 공백 값을 지우고자 한다면 replaceAll(" ", "") 을 사용할 수 있을 것 같아요!

.map(Integer::parseInt).collect(Collectors.toList());

inputView.guideToPutBonusNumber();
int bonusNumber = Integer.parseInt(inputView.acceptInput());

return new WinningLottoNumber(winningNumbers, bonusNumber);
}

public Winners selectWinners(Lotto lotto, WinningLottoNumber winningLottoNumber) {
WinnerSelector winnerSelector = new WinnerSelector(lotto, winningLottoNumber);
return winnerSelector.getWinners();
}
}
25 changes: 25 additions & 0 deletions src/main/java/ProfitCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
public class ProfitCalculator {

final static Integer FIRST_PRIZE = 2000000000;
final static Integer SECOND_PRIZE = 30000000;
final static Integer THIRD_PRIZE = 1500000;
final static Integer FOURTH_PRIZE = 50000;
final static Integer FIFTH_PRIZE = 5000;
Comment on lines +3 to +7
Copy link
Member

Choose a reason for hiding this comment

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

등수에 대한 내용과 상금에 대한 값을 Enum으로 정의해서 처리해볼 수도 있을 것 같아요!


private float profit = 0;

public ProfitCalculator(Winners winners, Integer investMoney) {
Integer totalWinningMoney =
winners.getFirstWinner() * FIRST_PRIZE
+ winners.getSecondWinner() * SECOND_PRIZE
+ winners.getThirdWinner() * THIRD_PRIZE
+ winners.getFourthWinner() * FOURTH_PRIZE
+ winners.getFifthWinner() * FIFTH_PRIZE;

this.profit = totalWinningMoney.floatValue() / investMoney.floatValue();
}

public float getProfit() {
return profit;
}
}
31 changes: 31 additions & 0 deletions src/main/java/ResultView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import java.util.List;

public class ResultView {
public void printPurchasedGameCount(Integer gameCount) {
System.out.println(gameCount + "개를 구매했습니다.");
}

public void printLottoGameNumbers(Lotto lotto) {
List<LottoGame> lottoGames = lotto.getLottoGames();
for (int i = 0; i < lottoGames .size(); i++) {
System.out.println(lottoGames.get(i));
}
}

public void printWinnerStatisticsMessage() {
System.out.println("당첨 통계");
System.out.println("---------");
}

public void printWinnerStatistics(Winners winners, ProfitCalculator profitCalculator) {
float profit = profitCalculator.getProfit();

System.out.println("3개 일치 (" + profitCalculator.FIFTH_PRIZE + "원)- " + winners.getFifthWinner()+"개");
System.out.println("4개 일치 (" + profitCalculator.FOURTH_PRIZE + "원)- " + winners.getFourthWinner()+"개");
System.out.println("5개 일치 (" + profitCalculator.THIRD_PRIZE + "원)- " + winners.getThirdWinner()+"개");
System.out.println("5개 일치, 보너스 볼 일치 (" + profitCalculator.SECOND_PRIZE + "원)- " + winners.getSecondWinner()+"개");
System.out.println("6개 일치 (" + profitCalculator.FIRST_PRIZE + "원)- " + winners.getFirstWinner()+"개");
Comment on lines +23 to +27
Copy link
Member

Choose a reason for hiding this comment

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

위 ProfitCalculator 객체에 단 리뷰에서처럼 등수에 대한 내용을 Enum으로 관리한다면 각 등수 별 일치 메세지도 담아서 처리해줄 수 있을 것 같아요~😄

System.out.println("profit = " + profit);
System.out.println(String.format("총 수익률은 %.2f 입니다.", profit));
}
}
89 changes: 89 additions & 0 deletions src/main/java/WinnerSelector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import java.util.List;

public class WinnerSelector {
Copy link
Member

Choose a reason for hiding this comment

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

해당 객체에서도 등수를 계산하기 위해 각각의 로또 번호의 매칭된 카운트를 직접 계산해주고 있는데, 이 부분도 Enum에서 매칭 카운트 값을 등수 값과 함께 관리하고 있다면 더 용이하게 구현해볼 수 있을 것 같아요!

private final Winners winners;

public Winners getWinners() {
return winners;
}

public WinnerSelector(Lotto lotto, WinningLottoNumber winningLottoNumber) {
int firstWinner = 0;
int secondWinner = 0;
int thirdWinner = 0;
int fourthWinner = 0;
int fifthWinner = 0;
Comment on lines +11 to +15
Copy link
Member

Choose a reason for hiding this comment

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

최대한 불변 변수를 사용해서 값을 구해보는 걸 추천드릴게요!

이는 내 코드가 그렇게 이상한가요? 도서에 4장. 불변 활용하기 장에서 자세한 내용을 다루고 있습니다!
가장 큰 이유는 가변 변수를 사용하게 되면 계속 변경되는 변수의 값을 따라가기 어려워지고, 변수의 값을 혼동할 수 있다는 점이 제일 와 닿았어요!


List<Integer> winningNumbers = winningLottoNumber.getWinningNumbers();
Integer bonusNumber = winningLottoNumber.getBonusNumber();

for (LottoGame lottoGame : lotto.getLottoGames()) {
Integer matchCount = 0;

List<Integer> gameNumbers = lottoGame.getGameNumbers();
matchCount = findWinningNumberInGameNumber(matchCount, winningNumbers, gameNumbers);

firstWinner = getFirstWinner(firstWinner, matchCount);
secondWinner = getSecondWinner(secondWinner, bonusNumber, matchCount, gameNumbers);
thirdWinner = getThirdWinner(thirdWinner, bonusNumber, matchCount, gameNumbers);
fourthWinner = getFourthWinner(fourthWinner, matchCount);
fifthWinner = getFifthWinner(fifthWinner, matchCount);
}
this.winners = new Winners(firstWinner, secondWinner, thirdWinner, fourthWinner, fifthWinner);
}

private int getFifthWinner(int fifthWinner, Integer matchCount) {
if (matchCount == 3) {
fifthWinner++;
}
return fifthWinner;
}

private int getFourthWinner(int fourthWinner, Integer matchCount) {
if (matchCount == 4) {
fourthWinner++;
}
return fourthWinner;
}

private int getThirdWinner(int thirdWinner, Integer bonusNumber, Integer matchCount, List<Integer> gameNumbers) {
if (matchCount == 5 && !isBonusNumberMatch(gameNumbers, bonusNumber)) {
thirdWinner++;
}
return thirdWinner;
}

private int getSecondWinner(int secondWinner, Integer bonusNumber, Integer matchCount, List<Integer> gameNumbers) {
if (matchCount == 5 && isBonusNumberMatch(gameNumbers, bonusNumber)) {
secondWinner++;
}
return secondWinner;
}

private int getFirstWinner(int firstWinner, Integer matchCount) {
if (matchCount == 6) {
firstWinner++;
}
return firstWinner;
}


private boolean isBonusNumberMatch(List<Integer> gameNumbers, Integer bonusNumber) {
return gameNumbers.contains(bonusNumber);
}

private Integer findWinningNumberInGameNumber(Integer matchCount, List<Integer> winningNumbers, List<Integer> gameNumbers) {
for (Integer number : gameNumbers) {
matchCount = increaseMatchCountIfIncludeGameNumber(matchCount, winningNumbers, number);
}
return matchCount;
}

private Integer increaseMatchCountIfIncludeGameNumber(Integer matchCount, List<Integer> winningNumbers, Integer number) {
if (winningNumbers.contains(number)) {
matchCount++;
}
return matchCount;
}

}
Loading