-
Notifications
You must be signed in to change notification settings - Fork 20
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
[2주차] 객체지향 코드연습 (Suehyun666) #31
base: main
Are you sure you want to change the base?
Changes from 17 commits
e6ecb6a
e287377
41845b7
69aba52
928bcc2
87adba7
26c32dd
355ebf0
619aae4
7aaa590
77bae68
6049a3f
a14bc17
bff4f55
0d78f68
20eb9f3
97b2cd3
1a2ebfe
1858311
fde267e
f208822
55be10e
a6c7390
a5440df
bcb6e36
e48a13c
3867749
6de8f99
771e99b
8382b15
64269f2
f018d51
89a61c1
f7c2bbf
3430382
743cfc9
732acb0
ec97241
2aa1c6f
01e7fe1
d2112f1
c1af815
96bbaf3
4a63945
e764b62
dfb20a1
bea7598
958f0a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
package lotto; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
// TODO: 프로그램 구현 | ||
LottoGame lottoGame = new LottoGame(); | ||
lottoGame.start(); | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package lotto; | ||
|
||
import camp.nextstep.edu.missionutils.Console; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public class InputHandler { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. public 메서드가 private 메서드보다 위에 위치시켜주세요. 라고 제가 알고있었는 데 제가 다시 찾아보니 기능별로 응집하는 것도 좋다는 말도 있네요. 저도 개발하다보면 public 메서드들이 위에 많아져서 그 안에서 호출하는 private 메서드들이 많아져서 리팩토링할 때 조금 힘들었습니다. |
||
private static final int WINNING_NUMBER_COUNT = 6; | ||
private static final int BONUS_NUMBER_MIN = 1; | ||
private static final int BONUS_NUMBER_MAX = 45; | ||
private final OutputHandler outputHandler; | ||
|
||
private List<Integer> parseWinningNumbers(String input) { | ||
String[] tokens = input.split(","); | ||
validateWinningNumbersCount(tokens); | ||
|
||
List<Integer> numbers = new ArrayList<>(); | ||
for (String token : tokens) { | ||
int number = Integer.parseInt(token.trim()); | ||
validateNumberRange(number); | ||
numbers.add(number); | ||
} | ||
|
||
validateNoDuplicates(numbers); | ||
return numbers; | ||
} | ||
|
||
private int parseBonusNumber(String input, List<Integer> winningNumbers) { | ||
int bonusNumber = Integer.parseInt(input.trim()); | ||
validateBonusNumber(bonusNumber, winningNumbers); | ||
return bonusNumber; | ||
} | ||
|
||
private void validateMoney(String input) { | ||
if (!input.matches("\\d+") || Integer.parseInt(input) < LottoGame.LOTTO_PRICE) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 노리신거라면 대단하신데 Integer.parseInt를 하기 전에 이렇게 숫자인 지 확인함으로써 NumberFormatException이 안발생하게 한거 좋은 것 같아요. |
||
throw new IllegalArgumentException("[ERROR] 유효한 금액을 입력해 주세요. 금액은 1000원 이상이어야 합니다."); | ||
} | ||
} | ||
|
||
private void validateWinningNumbersCount(String[] tokens) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 입력받는 클래스는 입력값에 대한 형태에 대한 예외처리를 처리하도록 하는 건 괜찮은 데 이제 비즈니스 적인 에외처리도 하고 있죠. |
||
if (tokens.length != WINNING_NUMBER_COUNT) { | ||
throw new IllegalArgumentException("[ERROR] 당첨 번호는 6개여야 합니다."); | ||
} | ||
} | ||
|
||
private void validateNumberRange(int number) { | ||
if (number < BONUS_NUMBER_MIN || number > BONUS_NUMBER_MAX) { | ||
throw new IllegalArgumentException("[ERROR] 번호는 1부터 45 사이여야 합니다."); | ||
} | ||
} | ||
|
||
private void validateNoDuplicates(List<Integer> numbers) { | ||
if (isDuplicated(numbers)) { | ||
throw new IllegalArgumentException("[ERROR] 중복된 번호가 있습니다."); | ||
} | ||
} | ||
|
||
private void validateBonusNumber(int bonusNumber, List<Integer> winningNumbers) { | ||
if (bonusNumber < BONUS_NUMBER_MIN || bonusNumber > BONUS_NUMBER_MAX || winningNumbers.contains(bonusNumber)) { | ||
throw new IllegalArgumentException("[ERROR] 보너스 번호는 1부터 45 사이의 당첨 번호와 중복되지 않는 번호여야 합니다."); | ||
} | ||
} | ||
|
||
private boolean isDuplicated(List<Integer> numbers) { | ||
Set<Integer> uniqueNumbers = new HashSet<>(numbers); | ||
return uniqueNumbers.size() != numbers.size(); | ||
} | ||
|
||
public InputHandler(OutputHandler outputHandler) { | ||
this.outputHandler = outputHandler; | ||
} | ||
|
||
public int getMoney() { | ||
outputHandler.printInputPrompt("구입 금액을 입력해 주세요."); | ||
String input = Console.readLine(); | ||
validateMoney(input); | ||
return Integer.parseInt(input); | ||
} | ||
|
||
public List<Integer> getWinningNumbers() { | ||
outputHandler.printInputPrompt("당첨 번호를 입력해 주세요. (예: 1,2,3,4,5,6)"); | ||
String input = Console.readLine(); | ||
return parseWinningNumbers(input); | ||
} | ||
|
||
public int getBonusNumber(List<Integer> winningNumbers) { | ||
outputHandler.printInputPrompt("보너스 번호를 입력해 주세요."); | ||
String input = Console.readLine(); | ||
return parseBonusNumber(input, winningNumbers); | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,30 @@ | ||
package lotto; | ||
|
||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public class Lotto { | ||
|
||
private final List<Integer> numbers; | ||
|
||
private void validate(List<Integer> numbers) { | ||
if (numbers.size() != 6 || isDuplicated(numbers)) { | ||
throw new IllegalArgumentException("[ERROR] 잘못된 로또 번호입니다."); | ||
} | ||
} | ||
|
||
private boolean isDuplicated(List<Integer> numbers) { | ||
Set<Integer> uniqueNumbers = new HashSet<>(numbers); | ||
return uniqueNumbers.size() != numbers.size(); | ||
} | ||
|
||
public Lotto(List<Integer> numbers) { | ||
validate(numbers); | ||
this.numbers = numbers; | ||
} | ||
|
||
private void validate(List<Integer> numbers) { | ||
if (numbers.size() != 6) { | ||
throw new IllegalArgumentException(); | ||
} | ||
public List<Integer> getNumbers() { | ||
return numbers; | ||
} | ||
|
||
// TODO: 추가 기능 구현 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package lotto; | ||
|
||
import java.util.List; | ||
|
||
public class LottoGame { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 클래스에 너무 많은 책임이 부여되어 있습니다.
이외에도 여러 책임이 있고 조금 분리해야할 필요성이 보입니다. |
||
|
||
private final InputHandler inputHandler; | ||
private final LottoGenerator lottoGenerator; | ||
private final ResultCalculator resultCalculator; | ||
private final OutputHandler outputHandler; | ||
|
||
public static final int LOTTO_PRICE = 1000; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 접근제어자를 public으로 선언한 이유가 있을까요? 내부에서만 사용하는 것이 좋아보이고, 외부에서 사용하면 안된다고 생각합니다. 전체 클래스에서 공통적으로 사용하려면, 상수를 관리할 객체를 생성해주세요 |
||
|
||
public LottoGame() { | ||
this.outputHandler = new OutputHandler(); | ||
this.inputHandler = new InputHandler(outputHandler); | ||
this.lottoGenerator = new LottoGenerator(); | ||
this.resultCalculator = new ResultCalculator(); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 의존성을 외부에서 주입시켜주려 노력해주세요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LottoGame이 자신이 사용할 객체를 직접 고르면 LottoGame은 이 객체들의 변동사항에 대해 밀접하게 영향을 받게됩니다. 그러면 어떻게 해결해야할까요? |
||
|
||
public void start() { | ||
// 구입 금액 입력 | ||
int money = inputHandler.getMoney(); | ||
int lottoCount = money / LOTTO_PRICE; | ||
|
||
// 로또 번호 자동 생성 | ||
List<Lotto> boughtLottos = lottoGenerator.generateLottos(lottoCount); | ||
|
||
// 구매한 로또 출력 | ||
outputHandler.printLottos(boughtLottos); | ||
|
||
// 당첨 번호와 보너스 번호 입력 | ||
List<Integer> winningNumbers = inputHandler.getWinningNumbers(); | ||
int bonusNumber = inputHandler.getBonusNumber(winningNumbers); | ||
|
||
// 당첨 결과 계산 및 출력 | ||
int[] matchCounts = resultCalculator.calculateResults(boughtLottos, winningNumbers, bonusNumber); | ||
outputHandler.printStatistics(matchCounts, lottoCount * LOTTO_PRICE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 주석 처리는 최종 결과물에선 없애주세요. 없어도 읽히는 코드를 원합니다! |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package lotto; | ||
import camp.nextstep.edu.missionutils.Randoms; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
|
||
public class LottoGenerator { | ||
private static final int LOTTO_NUMBER_MIN = 1; | ||
private static final int LOTTO_NUMBER_MAX = 45; | ||
private static final int LOTTO_NUMBER_COUNT = 6; | ||
|
||
public List<Lotto> generateLottos(int count) { | ||
return IntStream.range(0, count) | ||
.mapToObj(i -> new Lotto(Randoms.pickUniqueNumbersInRange(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_NUMBER_COUNT))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 로또를 생성할 때, 랜덤으로 번호를 생성하는 것을 Lotto 객체 외부에서 할 필요가 없을 것 같습니다. Lotto 내부에서 생성해도 괜찮을 것 같네요. |
||
.collect(Collectors.toList()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,29 @@ | ||||||||||||
package lotto; | ||||||||||||
|
||||||||||||
public class OutputHandler { | ||||||||||||
private static final int PRICE_THREE = 5000; | ||||||||||||
private static final int PRICE_FOUR = 50000; | ||||||||||||
private static final int PRICE_FIVE = 1500000; | ||||||||||||
private static final int PRICE_FIVE_BONUS = 30000000; | ||||||||||||
private static final int PRICE_SIX = 2000000000; | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 출력 객체에서 당첨 금액을 관리하고 있는 것은 적절하지 않은 것 같습니다. 당첨 금액을 관리할 객체를 추가해주세요 |
||||||||||||
|
||||||||||||
public void printLottos(java.util.List<Lotto> lottos) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. import가 안된 것 같습니다.
Suggested change
|
||||||||||||
System.out.println(lottos.size() + "개를 구매했습니다."); | ||||||||||||
lottos.forEach(lotto -> System.out.println(lotto.getNumbers())); | ||||||||||||
} | ||||||||||||
public void printInputPrompt(String message) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
컨벤션 지켜주세요 |
||||||||||||
System.out.println(message); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public void printStatistics(int[] matchCounts, int totalCost) { | ||||||||||||
System.out.println("3개 일치 (5,000원) - " + matchCounts[3] + "개"); | ||||||||||||
System.out.println("4개 일치 (50,000원) - " + matchCounts[4] + "개"); | ||||||||||||
System.out.println("5개 일치 (1,500,000원) - " + matchCounts[5] + "개"); | ||||||||||||
System.out.println("5개 일치, 보너스 볼 일치 (30,000,000원) - " + matchCounts[7] + "개"); | ||||||||||||
System.out.println("6개 일치 (2,000,000,000원) - " + matchCounts[6] + "개"); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위에 선제멘토님이 말한대로 matchCounts[] 라는 int[]을 사용하니까 앞으로의 유지보수가 좋아보이지 않아요. |
||||||||||||
|
||||||||||||
double totalPrize = matchCounts[3] * PRICE_THREE + matchCounts[4] * PRICE_FOUR + matchCounts[5] * PRICE_FIVE + matchCounts[7] * PRICE_FIVE_BONUS + matchCounts[6] * PRICE_SIX; | ||||||||||||
double profitRate = (totalPrize / totalCost) * 100; | ||||||||||||
System.out.printf("총 수익률은 %.1f%%입니다.%n", profitRate); | ||||||||||||
} | ||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,44 @@ | ||||||||||||||||||
package lotto; | ||||||||||||||||||
|
||||||||||||||||||
import java.util.List; | ||||||||||||||||||
|
||||||||||||||||||
public class ResultCalculator { | ||||||||||||||||||
private static final int MATCH_THREE = 3; | ||||||||||||||||||
private static final int MATCH_FOUR = 4; | ||||||||||||||||||
private static final int MATCH_FIVE = 5; | ||||||||||||||||||
private static final int MATCH_SIX = 6; | ||||||||||||||||||
private static final int MATCH_FIVE_BONUS = 7; // 5개 + 보너스 일치 | ||||||||||||||||||
|
||||||||||||||||||
private void incrementCount(int[] matchCounts, int index) { | ||||||||||||||||||
if (index >= 0) { | ||||||||||||||||||
matchCounts[index]++; | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private int getMatchCount(List<Integer> numbers, List<Integer> winningNumbers) { | ||||||||||||||||||
return (int) numbers.stream().filter(winningNumbers::contains).count(); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
private int calculateResultIndex(int matchCount, boolean bonusMatch) { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. private 메소드 전부 public 메소드 아래에 위치하도록 해주세요! |
||||||||||||||||||
if (matchCount == MATCH_SIX) {return MATCH_SIX; // 6개 일치 | ||||||||||||||||||
} if (matchCount == MATCH_FIVE && bonusMatch) {return MATCH_FIVE_BONUS; // 5개 + 보너스 일치 | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 가독성이 떨어지는 것 같습니다.
Suggested change
|
||||||||||||||||||
} if (matchCount == MATCH_FIVE) {return MATCH_FIVE; // 5개 일치 | ||||||||||||||||||
} if (matchCount == MATCH_FOUR) {return MATCH_FOUR; // 4개 일치 | ||||||||||||||||||
} if (matchCount == MATCH_THREE) {return MATCH_THREE; // 3개 일치 | ||||||||||||||||||
}return -1; // 일치하지 않는 경우 | ||||||||||||||||||
|
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
public int[] calculateResults(List<Lotto> boughtLottos, List<Integer> winningNumbers, int bonusNumber) { | ||||||||||||||||||
int[] matchCounts = new int[8]; | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 8이 무슨 의미인지 모르겠어요. |
||||||||||||||||||
|
||||||||||||||||||
for (Lotto lotto : boughtLottos) { | ||||||||||||||||||
int matchCount = getMatchCount(lotto.getNumbers(), winningNumbers); | ||||||||||||||||||
boolean bonusMatch = lotto.getNumbers().contains(bonusNumber); | ||||||||||||||||||
|
||||||||||||||||||
int resultIndex = calculateResultIndex(matchCount, bonusMatch); | ||||||||||||||||||
incrementCount(matchCounts, resultIndex); | ||||||||||||||||||
} | ||||||||||||||||||
return matchCounts; | ||||||||||||||||||
} | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
줄바꿈 신경써주세요.