-
Notifications
You must be signed in to change notification settings - Fork 92
[그리디] 김태우 로또 미션 3,4,5 단계 제출합니다. #146
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
Changes from 48 commits
849e2fe
84ad2df
cf566ce
84fe5a6
ee9214d
b0320e9
caad7b4
3625fd2
3c52ba5
5256990
6eaeb8f
35d5b27
1925a09
59a8dbd
3960e78
8f78037
bf9eb51
8a1ed8f
ea203ae
53e53c6
c0d24e0
2578a3e
3b73ece
8f4157d
fa8c3ae
dcc3c70
32ff258
0293eee
5dcf37f
a45f213
d87205d
98ea697
036ccdb
510b0c0
9f18556
f74a8ac
fd6c52f
87965f2
3c5d1a1
eb187e4
6d47438
de36cc9
2393907
b32bfa9
28647f0
579c2f3
450573a
022567f
fb04843
e1680af
e4cddbd
6a4da86
58fe1f0
7b87bf7
5cda9d9
679ffc8
f0de06b
65046e1
243dcd2
73c7c05
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 |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # 로또 미션 | ||
|
|
||
| ## 기능 요구사항 | ||
|
|
||
| - 로또 당첨 번호를 받아 일치한 번호 수에 따라 당첨 결과를 보여준다. | ||
|
|
||
| ## 프로그래밍 요구사항 | ||
|
|
||
| - 모든 원시 값과 문자열을 포장한다. | ||
| - 일급 컬렉션을 쓴다. | ||
|
|
||
| ## 추가 기능 요구사항 | ||
|
|
||
| - 2등을 위한 보너스볼을 추첨한다. | ||
| - 당첨 통계에 2등을 추가한다. | ||
| - 2등 당첨 조건은 당첨 번호 5개 + 보너스 볼이다 | ||
| - 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다. | ||
| - 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import controller.LottoController; | ||
| import view.PurchaseAmount; | ||
| import model.LottoNumber; | ||
| import model.LottoNumbers; | ||
| import model.LottoTicketBundle; | ||
|
|
||
| public class Main { | ||
| public static void main(String[] args) { | ||
| LottoController controller = new LottoController(); | ||
|
|
||
| // 1. 구입 금액 입력 | ||
| PurchaseAmount purchaseAmount = controller.askPurchaseAmount(); | ||
|
|
||
| // 2. 수동 및 자동 로또 번호 생성 | ||
| LottoTicketBundle allTickets = controller.buyTickets(purchaseAmount.howManyLottos()); | ||
|
|
||
| // 3. 당첨 번호 입력 | ||
| LottoNumbers winningNumbers = controller.askWinningNumbers(); | ||
|
|
||
| // 4. 보너스 볼 입력 | ||
| LottoNumber bonusBall = controller.askBonusBall(); | ||
|
|
||
| // 5. 통계 출력 | ||
| controller.showStatistics(allTickets.readLottoNumbersRepository(), | ||
| winningNumbers.getNumbers(), | ||
| purchaseAmount.getValue(), | ||
| bonusBall | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| package controller; | ||
|
|
||
| import view.InputView; | ||
| import view.OutputView; | ||
| import view.PurchaseAmount; | ||
| import model.LottoNumber; | ||
| import model.LottoNumbers; | ||
| import model.LottoService; | ||
| import model.LottoTicketBundle; | ||
| import model.MatchResult; | ||
|
|
||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public class LottoController { | ||
| private final LottoService lottoService = new LottoService(); | ||
| private final OutputView outView = new OutputView(); | ||
| private final InputView inputView = new InputView(); | ||
|
|
||
| private PurchaseAmount readPrice() { | ||
| outView.printInputPrice(); | ||
| while (true) { | ||
| PurchaseAmount purchaseAmount = readPriceException(); | ||
| if (purchaseAmount != null) return purchaseAmount; | ||
| } | ||
| } | ||
|
|
||
| private PurchaseAmount readPriceException() { | ||
| try { | ||
| return new PurchaseAmount(Integer.parseInt(inputView.inputPrice())); | ||
| } catch (NumberFormatException input) { | ||
| outView.printInvalidNumber(); | ||
| } catch (IllegalArgumentException input) { | ||
| outView.printInvalidPrice(); | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| public PurchaseAmount askPurchaseAmount() { | ||
| PurchaseAmount purchaseAmount = readPrice(); | ||
| outView.printPriceValue(purchaseAmount.getValue()); | ||
| System.out.println(); | ||
| return purchaseAmount; | ||
| } | ||
|
|
||
| public LottoTicketBundle buyTickets(int ticketCount) { | ||
| int manualCount = requestManualTicketCount(); | ||
| int autoCount = calculateAutoCount(ticketCount, manualCount); | ||
|
|
||
| LottoTicketBundle tickets = generateTickets(manualCount, autoCount); | ||
| displayTickets(manualCount, autoCount, tickets); | ||
|
|
||
| return tickets; | ||
| } | ||
|
|
||
| private int requestManualTicketCount() { | ||
| outView.printManualTicketCount(); | ||
| return Integer.parseInt(inputView.inputManualCount()); | ||
| } | ||
|
|
||
| private int calculateAutoCount(int totalTickets, int manualCount) { | ||
| return totalTickets - manualCount; | ||
| } | ||
|
|
||
| private LottoTicketBundle generateTickets(int manualCount, int autoCount) { | ||
| outView.printManualLottoNumbersPrompt(); | ||
| List<String> manualInputs = inputView.inputManualLottos(manualCount); | ||
|
|
||
| LottoTicketBundle manualTickets = lottoService.createManualLottos(manualInputs); | ||
| LottoTicketBundle autoTickets = lottoService.createLottos(autoCount); | ||
|
|
||
| return lottoService.mergeAutoAndManualLottos(manualTickets, autoTickets); | ||
| } | ||
|
|
||
| private void displayTickets(int manualCount, int autoCount, LottoTicketBundle tickets) { | ||
| outView.printBuyCount(manualCount, autoCount); | ||
| outView.printLottos(tickets.readLottoNumbersRepository()); | ||
| } | ||
|
|
||
| public LottoNumbers askWinningNumbers() { | ||
| outView.printInputWinningNumbers(); | ||
| LottoNumbers lastLotto = lottoService.createInputLotto(inputView.inputWinningNumbers()); | ||
| System.out.println(); | ||
| return lastLotto; | ||
| } | ||
|
|
||
| public LottoNumber askBonusBall() { | ||
| outView.printBonusBall(); | ||
| LottoNumber bonusNumber = new LottoNumber(Integer.parseInt(inputView.inputBonusBall())); | ||
| System.out.println(); | ||
| return bonusNumber; | ||
| } | ||
|
|
||
| public void showStatistics(List<LottoNumbers> allTickets, List<LottoNumber> winningNumbers, int purchaseAmount, LottoNumber bonusBall) { | ||
| Map<MatchResult, Integer> matchCounts = lottoService.countMatchResults(allTickets, winningNumbers, bonusBall); | ||
| String profitRate = lottoService.calculateProfitRate(matchCounts, purchaseAmount); | ||
| outView.printLotteryStatistics(matchCounts, profitRate); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package model; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class FixedLottoNumberGenerator implements LottoNumberGenerator { | ||
| private final List<LottoNumber> fixedNumbers; | ||
|
|
||
| public FixedLottoNumberGenerator(List<Integer> numbers) { | ||
| if (numbers.size() != 6) { | ||
| throw new IllegalArgumentException("로또 번호는 6개여야 합니다."); | ||
| } | ||
| this.fixedNumbers = numbers.stream() | ||
| .map(LottoNumber::new) | ||
| .toList(); | ||
| } | ||
|
|
||
| @Override | ||
| public LottoNumbers generate() { | ||
| return new LottoNumbers(fixedNumbers); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package model; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| public class LottoNumber { | ||
| private final int number; | ||
|
|
||
| public LottoNumber(int number) { | ||
| validateRange(number); | ||
| this.number = number; | ||
| } | ||
|
|
||
| private void validateRange(int number) { | ||
| if (number < 1 || number > 45) { | ||
| throw new IllegalArgumentException("로또 번호는 1~45 사이여야 합니다"); | ||
| } | ||
| } | ||
|
|
||
| public int getNumber() { | ||
| return number; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return String.valueOf(number); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) return true; | ||
| if (!(o instanceof LottoNumber)) return false; | ||
| LottoNumber lottoNumber = (LottoNumber) o; | ||
| return number == lottoNumber.number; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(number); | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package model; | ||
|
|
||
| public interface LottoNumberGenerator { | ||
| LottoNumbers generate(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package model; | ||
|
|
||
| import java.util.*; | ||
|
|
||
| public class LottoNumbers { | ||
| private final List<LottoNumber> numbers; | ||
|
|
||
| public LottoNumbers(List<LottoNumber> numbers) { | ||
| validateSize(numbers); | ||
| this.numbers = List.copyOf(numbers); | ||
| } | ||
|
|
||
| private void validateSize(List<LottoNumber> numbers) { | ||
| if (numbers.size() != 6) { | ||
| throw new IllegalArgumentException("로또 번호가 6개가 아닙니다"); | ||
| } | ||
| } | ||
|
|
||
| public LottoNumbers sortNumbers() { | ||
| List<LottoNumber> sortNumber = new ArrayList<>(numbers); | ||
| Collections.sort(sortNumber, Comparator.comparingInt(LottoNumber::getNumber)); | ||
| return new LottoNumbers(sortNumber); | ||
| } | ||
|
|
||
| public List<LottoNumber> getNumbers() { | ||
| return numbers; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (this == o) return true; | ||
| if (!(o instanceof LottoNumbers)) return false; | ||
| LottoNumbers lottoNumbers = (LottoNumbers) o; | ||
| return Objects.equals(numbers, lottoNumbers.numbers); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(numbers); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| package model; | ||
|
|
||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.EnumMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public class LottoService { | ||
| private final LottoNumberGenerator generator; | ||
|
|
||
| public LottoService() { | ||
| this(new RandomLottoNumberGenerator()); | ||
| } | ||
|
|
||
| public LottoService(LottoNumberGenerator generator) { | ||
| this.generator = generator; | ||
| } | ||
|
|
||
| public LottoNumbers createOneLotto() { | ||
| return generator.generate(); | ||
| } | ||
|
|
||
| public LottoTicketBundle createLottos(int count) { | ||
| LottoTicketBundle repository = new LottoTicketBundle(); | ||
| for (int i = 0; i < count; i++) { | ||
| repository.addLottoNumbers(createOneLotto().sortNumbers()); | ||
| } | ||
| return repository; | ||
| } | ||
|
|
||
| public LottoNumbers createInputLotto(String inputNumber) { | ||
| List<LottoNumber> numbers = parseInputToNumbers(inputNumber); | ||
| return new LottoNumbers(numbers); | ||
| } | ||
|
|
||
| private List<LottoNumber> parseInputToNumbers(String inputNumber) { | ||
| String[] rawNumbers = inputNumber.split(","); | ||
| List<LottoNumber> numbers = new ArrayList<>(); | ||
| for (String raw : rawNumbers) { | ||
| numbers.add(new LottoNumber(Integer.parseInt(raw.trim()))); | ||
| } | ||
| return numbers; | ||
| } | ||
|
|
||
| public LottoTicketBundle createManualLottos(List<String> inputs) { | ||
| LottoTicketBundle repository = new LottoTicketBundle(); | ||
| for (String input : inputs) { | ||
| repository.addLottoNumbers(createInputLotto(input)); | ||
| } | ||
| return repository; | ||
| } | ||
|
|
||
| public LottoTicketBundle mergeAutoAndManualLottos(LottoTicketBundle manual, LottoTicketBundle auto) { | ||
| LottoTicketBundle repository = new LottoTicketBundle(); | ||
| for (LottoNumbers lotto : manual.readLottoNumbersRepository()) { | ||
| repository.addLottoNumbers(lotto); | ||
| } | ||
|
|
||
| for (LottoNumbers lotto : auto.readLottoNumbersRepository()) { | ||
| repository.addLottoNumbers(lotto); | ||
| } | ||
| return repository; | ||
| } | ||
|
|
||
| public Map<MatchResult, Integer> countMatchResults(List<LottoNumbers> allLotteries, | ||
| List<LottoNumber> lastLotto, | ||
| LottoNumber bonusBall) { | ||
| Map<MatchResult, Integer> matchCounts = initializeMatchCounts(); | ||
| for (LottoNumbers oneLotto : allLotteries) { | ||
| int count = matchLottoNumber(oneLotto.getNumbers(), lastLotto); | ||
| boolean bonusMatch = matchBonus(oneLotto.getNumbers(), bonusBall, count); | ||
| MatchResult result = MatchResult.fromCount(count, bonusMatch); | ||
| matchCounts.put(result, matchCounts.get(result) + 1); | ||
| } | ||
| return matchCounts; | ||
| } | ||
|
|
||
| private Map<MatchResult, Integer> initializeMatchCounts() { | ||
| Map<MatchResult, Integer> counts = new EnumMap<>(MatchResult.class); | ||
| for (MatchResult result : MatchResult.values()) { | ||
| counts.put(result, 0); | ||
| } | ||
| return counts; | ||
| } | ||
|
|
||
| private int matchLottoNumber(List<LottoNumber> oneLotto, List<LottoNumber> lastLotto) { | ||
|
||
| List<Integer> lastNumbers = lastLotto.stream() | ||
| .map(LottoNumber::getNumber) | ||
| .toList(); | ||
| int matchCount = 0; | ||
| for (LottoNumber number : oneLotto) { | ||
| matchCount += containsNumber(lastNumbers, number); | ||
| } | ||
| return matchCount; | ||
| } | ||
|
|
||
| private int containsNumber(List<Integer> lastNumbers, LottoNumber number) { | ||
| if (lastNumbers.contains(number.getNumber())) return 1; | ||
|
||
| return 0; | ||
| } | ||
|
|
||
| private boolean matchBonus(List<LottoNumber> oneLotto, LottoNumber bonusBall, int count) { | ||
| if (count == 5) { | ||
| return oneLotto.contains(bonusBall); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| public String calculateProfitRate(Map<MatchResult, Integer> matchCounts, int money) { | ||
| double profitRate = 0; | ||
| for (MatchResult result : matchCounts.keySet()) { | ||
| profitRate += matchCounts.get(result) * result.getReward(); | ||
| } | ||
| profitRate = profitRate / (double) money; | ||
| return String.format("%.2f", profitRate); | ||
| } | ||
| } | ||
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.
전체적으로 Controller 클래스임에도 출력문이 직접적으로 작성되어 있는 것 같아요!
MVC 패턴을 적용시키신 김에 출력의 책임은 컨트롤러에서 제외하는 것이 어떨까요?
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.
저는 Controller가 출력까지 책임지는 것으로 이해하고 있었는데,
상희님 말씀대로라면 Controller가 그 책임을 갖지 않는 구조로 보입니다.
혹시 출력의 책임은 어느 계층에서 가져가는 게 더 적절할지 조언해주실 수 있을까요?
Uh oh!
There was an error while loading. Please reload this page.
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.
제가 말한 부분은 아래 코드인데요, 현재 print문은 모두 OutputView에서 작성해주신 것 같아요!
컨트롤러에서의 print문은 제거하고, OutputView가 출력의 책임을 모두 담당하게 하는 것이 어떨까요?
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.
view와 controller의 역할을 분리하려고 신경 썼는데, 말씀해주신 부분은 놓쳤네요!
System.out.println();을 printBlankLine() 메서드로 분리해서 OutputView에서 책임을 가지도록 수정했습니다.