From f305c75d96b816dffb890845d83f54ea9c7924e3 Mon Sep 17 00:00:00 2001 From: yaevrai Date: Sat, 24 May 2025 18:02:58 +0900 Subject: [PATCH 1/5] =?UTF-8?q?docs:=20README.md=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..5d6e380ca --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# java-lotto : 로또 - 클린코드 + +## Level1. 로또 자동 구매 + +### - 요구사항 + +- [ ] 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다. +- [ ] 로또 1장의 가격은 1000원이다. + +### - 새로운 프로그래밍 요구사항 + +- [ ] 배열 대신 컬렉션을 사용한다. +- [ ] 줄여 쓰지 않는다(축약 금지). +- [ ] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. +- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. + +## Level2. 로또 당첨 + +### - 요구사항 + +- [ ] 로또 당첨 번호를 받아 일치한 번호 수에 따라 당첨 결과를 보여준다. + +### - 새로운 프로그래밍 요구사항 + +- [ ] 모든 원시 값과 문자열을 포장한다. +- [ ] 일급 컬렉션을 쓴다. + +## Level3. 로또 2등 당첨 + +### - 요구사항 + +- [ ] 2등을 위한 보너스볼을 추첨한다. +- [ ] 당첨 통계에 2등을 추가한다. +- [ ] 2등 당첨 조건은 당첨 번호 5개 일치 + 보너스 볼 일치다. + +### - 새로운 프로그래밍 요구사항 + +- [ ] Java Enum을 적용한다. + +## Level4. 로또 수동 구매 + +### - 요구사항 + +- [ ] 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다. +- [ ] 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다. + +## Level5. 리팩토링 + +### - 새로운 프로그래밍 요구사항 + +- [ ] 기존 프로그래밍 요구사항을 다시 한번 확인하고, 학습 테스트를 통해 학습한 내용을 반영한다. + +### - 기존 프로그래밍 요구사항 + +- [ ] 자바 코드 컨벤션을 지키면서 프로그래밍한다. +- [ ] 기본적으로 Java Style Guide을 원칙으로 한다. +- [ ] indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. +- [ ] 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다. 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 + 된다. +- [ ] 3항 연산자를 쓰지 않는다. +- [ ] else 예약어를 쓰지 않는다. +- [ ] else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다. 힌트: if문에서 값을 반환하는 방식으로 + 구현하면 else 예약어를 사용하지 않아도 된다. +- [ ] 배열 대신 컬렉션을 사용한다. +- [ ] 줄여 쓰지 않는다(축약 금지). +- [ ] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. +- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. +- [ ] 모든 원시 값과 문자열을 포장한다. +- [ ] 일급 컬렉션을 쓴다. +- [ ] Java Enum을 적용한다. From 8f271dd066a716545bf61b3f467d16e4f6d42b9d Mon Sep 17 00:00:00 2001 From: yaevrai Date: Sat, 24 May 2025 18:56:52 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat(level1):=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EA=B5=AC=EB=A7=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 +++---- src/main/java/Lotto.java | 14 ++++++++ src/main/java/LottoController.java | 11 ++++++ src/main/java/LottoInputView.java | 12 +++++++ src/main/java/LottoOutputView.java | 14 ++++++++ src/main/java/LottoService.java | 54 ++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 src/main/java/Lotto.java create mode 100644 src/main/java/LottoController.java create mode 100644 src/main/java/LottoInputView.java create mode 100644 src/main/java/LottoOutputView.java create mode 100644 src/main/java/LottoService.java diff --git a/README.md b/README.md index 5d6e380ca..5f382bfd3 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,15 @@ ### - 요구사항 -- [ ] 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다. -- [ ] 로또 1장의 가격은 1000원이다. +- [x] 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다. +- [x] 로또 1장의 가격은 1000원이다. ### - 새로운 프로그래밍 요구사항 -- [ ] 배열 대신 컬렉션을 사용한다. -- [ ] 줄여 쓰지 않는다(축약 금지). -- [ ] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. -- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. +- [x] 배열 대신 컬렉션을 사용한다. +- [x] 줄여 쓰지 않는다(축약 금지). +- [x] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. +- [x] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. ## Level2. 로또 당첨 diff --git a/src/main/java/Lotto.java b/src/main/java/Lotto.java new file mode 100644 index 000000000..1e87a5836 --- /dev/null +++ b/src/main/java/Lotto.java @@ -0,0 +1,14 @@ +import java.util.List; + +public class Lotto { + + private final List numbers; + + public Lotto(List numbers) { + this.numbers = numbers; + } + + public List getNumbers() { + return numbers; + } +} diff --git a/src/main/java/LottoController.java b/src/main/java/LottoController.java new file mode 100644 index 000000000..da4a22aa9 --- /dev/null +++ b/src/main/java/LottoController.java @@ -0,0 +1,11 @@ +public class LottoController { + + public static void main(String[] args) { + LottoInputView inputView = new LottoInputView(); + LottoOutputView outputView = new LottoOutputView(); + + LottoService service = new LottoService(inputView, outputView); + + service.start(); + } +} diff --git a/src/main/java/LottoInputView.java b/src/main/java/LottoInputView.java new file mode 100644 index 000000000..88ce687f2 --- /dev/null +++ b/src/main/java/LottoInputView.java @@ -0,0 +1,12 @@ +import java.util.Scanner; + +public class LottoInputView { + + public int inputMoney() { + System.out.println("구입금액을 입력해 주세요."); + Scanner scanner = new Scanner(System.in); + + return scanner.nextInt(); + } + +} diff --git a/src/main/java/LottoOutputView.java b/src/main/java/LottoOutputView.java new file mode 100644 index 000000000..5253905c4 --- /dev/null +++ b/src/main/java/LottoOutputView.java @@ -0,0 +1,14 @@ +import java.util.List; + +public class LottoOutputView { + + public void printPurchasedLottoCount(int count) { + System.out.println(count + "개를 구매했습니다."); + } + + public void printLottos(List lottos) { + for (Lotto lotto : lottos) { + System.out.println(lotto.getNumbers()); + } + } +} diff --git a/src/main/java/LottoService.java b/src/main/java/LottoService.java new file mode 100644 index 000000000..1aa3cfcbf --- /dev/null +++ b/src/main/java/LottoService.java @@ -0,0 +1,54 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class LottoService { + + private static final int PRICE = 1000; + + private final LottoInputView inputView; + private final LottoOutputView outputView; + + public LottoService(LottoInputView inputView, LottoOutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void start() { + int money = inputView.inputMoney(); + int amount = money / PRICE; + + List lottos = purchaseLotto(amount); + outputView.printPurchasedLottoCount(amount); + outputView.printLottos(lottos); + } + + private List purchaseLotto(int amount) { + List lottos = new ArrayList<>(); + for (int i = 0; i < amount; i++) { + lottos.add(new Lotto(createLottoNumbers())); + } + return lottos; + } + + private List createLottoNumbers() { + List numbers = new ArrayList<>(); + + for (int i = 1; i <= 45; i++) { + numbers.add(i); + } + + Collections.shuffle(numbers); + + return pickFirstSixSorted(numbers); + } + + private List pickFirstSixSorted(List shuffled) { + List result = new ArrayList<>(); + for (int i = 0; i < 6; i++) { + result.add(shuffled.get(i)); + } + Collections.sort(result); + return result; + } +} From 4e6f699e894c0d3d79fc0d0600258f16dbb2402a Mon Sep 17 00:00:00 2001 From: yaevrai Date: Mon, 26 May 2025 20:34:33 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat(level2):=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=8B=B9=EC=B2=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Lotto.java | 14 ----- src/main/java/LottoController.java | 11 ---- src/main/java/LottoInputView.java | 12 ---- src/main/java/LottoOutputView.java | 14 ----- src/main/java/LottoService.java | 54 ------------------ src/main/java/lotto/LottoController.java | 12 ++++ src/main/java/lotto/LottoService.java | 56 +++++++++++++++++++ src/main/java/lotto/model/Lotto.java | 26 +++++++++ src/main/java/lotto/model/LottoGenerator.java | 33 +++++++++++ src/main/java/lotto/model/LottoNumbers.java | 26 +++++++++ src/main/java/lotto/model/LottoTicket.java | 14 +++++ src/main/java/lotto/model/MatchCount.java | 14 +++++ src/main/java/lotto/model/Money.java | 20 +++++++ src/main/java/lotto/model/WinningNumbers.java | 17 ++++++ src/main/java/lotto/model/WinningResult.java | 49 ++++++++++++++++ src/main/java/lotto/view/LottoInputView.java | 30 ++++++++++ src/main/java/lotto/view/LottoOutputView.java | 32 +++++++++++ 17 files changed, 329 insertions(+), 105 deletions(-) delete mode 100644 src/main/java/Lotto.java delete mode 100644 src/main/java/LottoController.java delete mode 100644 src/main/java/LottoInputView.java delete mode 100644 src/main/java/LottoOutputView.java delete mode 100644 src/main/java/LottoService.java create mode 100644 src/main/java/lotto/LottoController.java create mode 100644 src/main/java/lotto/LottoService.java create mode 100644 src/main/java/lotto/model/Lotto.java create mode 100644 src/main/java/lotto/model/LottoGenerator.java create mode 100644 src/main/java/lotto/model/LottoNumbers.java create mode 100644 src/main/java/lotto/model/LottoTicket.java create mode 100644 src/main/java/lotto/model/MatchCount.java create mode 100644 src/main/java/lotto/model/Money.java create mode 100644 src/main/java/lotto/model/WinningNumbers.java create mode 100644 src/main/java/lotto/model/WinningResult.java create mode 100644 src/main/java/lotto/view/LottoInputView.java create mode 100644 src/main/java/lotto/view/LottoOutputView.java diff --git a/src/main/java/Lotto.java b/src/main/java/Lotto.java deleted file mode 100644 index 1e87a5836..000000000 --- a/src/main/java/Lotto.java +++ /dev/null @@ -1,14 +0,0 @@ -import java.util.List; - -public class Lotto { - - private final List numbers; - - public Lotto(List numbers) { - this.numbers = numbers; - } - - public List getNumbers() { - return numbers; - } -} diff --git a/src/main/java/LottoController.java b/src/main/java/LottoController.java deleted file mode 100644 index da4a22aa9..000000000 --- a/src/main/java/LottoController.java +++ /dev/null @@ -1,11 +0,0 @@ -public class LottoController { - - public static void main(String[] args) { - LottoInputView inputView = new LottoInputView(); - LottoOutputView outputView = new LottoOutputView(); - - LottoService service = new LottoService(inputView, outputView); - - service.start(); - } -} diff --git a/src/main/java/LottoInputView.java b/src/main/java/LottoInputView.java deleted file mode 100644 index 88ce687f2..000000000 --- a/src/main/java/LottoInputView.java +++ /dev/null @@ -1,12 +0,0 @@ -import java.util.Scanner; - -public class LottoInputView { - - public int inputMoney() { - System.out.println("구입금액을 입력해 주세요."); - Scanner scanner = new Scanner(System.in); - - return scanner.nextInt(); - } - -} diff --git a/src/main/java/LottoOutputView.java b/src/main/java/LottoOutputView.java deleted file mode 100644 index 5253905c4..000000000 --- a/src/main/java/LottoOutputView.java +++ /dev/null @@ -1,14 +0,0 @@ -import java.util.List; - -public class LottoOutputView { - - public void printPurchasedLottoCount(int count) { - System.out.println(count + "개를 구매했습니다."); - } - - public void printLottos(List lottos) { - for (Lotto lotto : lottos) { - System.out.println(lotto.getNumbers()); - } - } -} diff --git a/src/main/java/LottoService.java b/src/main/java/LottoService.java deleted file mode 100644 index 1aa3cfcbf..000000000 --- a/src/main/java/LottoService.java +++ /dev/null @@ -1,54 +0,0 @@ -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class LottoService { - - private static final int PRICE = 1000; - - private final LottoInputView inputView; - private final LottoOutputView outputView; - - public LottoService(LottoInputView inputView, LottoOutputView outputView) { - this.inputView = inputView; - this.outputView = outputView; - } - - public void start() { - int money = inputView.inputMoney(); - int amount = money / PRICE; - - List lottos = purchaseLotto(amount); - outputView.printPurchasedLottoCount(amount); - outputView.printLottos(lottos); - } - - private List purchaseLotto(int amount) { - List lottos = new ArrayList<>(); - for (int i = 0; i < amount; i++) { - lottos.add(new Lotto(createLottoNumbers())); - } - return lottos; - } - - private List createLottoNumbers() { - List numbers = new ArrayList<>(); - - for (int i = 1; i <= 45; i++) { - numbers.add(i); - } - - Collections.shuffle(numbers); - - return pickFirstSixSorted(numbers); - } - - private List pickFirstSixSorted(List shuffled) { - List result = new ArrayList<>(); - for (int i = 0; i < 6; i++) { - result.add(shuffled.get(i)); - } - Collections.sort(result); - return result; - } -} diff --git a/src/main/java/lotto/LottoController.java b/src/main/java/lotto/LottoController.java new file mode 100644 index 000000000..9b8cc8921 --- /dev/null +++ b/src/main/java/lotto/LottoController.java @@ -0,0 +1,12 @@ +package lotto; + +import lotto.view.LottoInputView; +import lotto.view.LottoOutputView; + +public class LottoController { + + public static void main(String[] args) { + LottoService service = new LottoService(new LottoInputView(), new LottoOutputView()); + service.start(); + } +} diff --git a/src/main/java/lotto/LottoService.java b/src/main/java/lotto/LottoService.java new file mode 100644 index 000000000..7636edd4b --- /dev/null +++ b/src/main/java/lotto/LottoService.java @@ -0,0 +1,56 @@ +package lotto; + +import java.util.List; +import java.util.stream.Collectors; +import lotto.model.Lotto; +import lotto.model.LottoGenerator; +import lotto.model.LottoTicket; +import lotto.model.MatchCount; +import lotto.model.Money; +import lotto.model.WinningNumbers; +import lotto.model.WinningResult; +import lotto.view.LottoInputView; +import lotto.view.LottoOutputView; + +public class LottoService { + + private final LottoInputView inputView; + private final LottoOutputView outputView; + private final LottoGenerator lottoGenerator; + + public LottoService(LottoInputView inputView, LottoOutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + this.lottoGenerator = new LottoGenerator(); + } + + public void start() { + Money purchaseMoney = new Money(inputView.inputMoney()); + + List lottos = purchaseLottos(purchaseMoney); + + LottoTicket lottoTicket = purchaseMoney.calculateTicketCount(); + + outputView.printPurchasedLottoCount(lottoTicket.getCount()); + outputView.printLottos(lottos); + + WinningNumbers winningNumbers = new WinningNumbers(inputView.inputWinningNumber()); + + WinningResult winningResult = calculateWinningResult(lottos, winningNumbers); + + outputView.printWinningStatistics(purchaseMoney.getValue(), + winningResult.getWinningStatistics()); + } + + private List purchaseLottos(Money money) { + return lottoGenerator.generate(money.calculateTicketCount().getCount()); + } + + private WinningResult calculateWinningResult(List lottos, WinningNumbers winningNumbers) { + List matchCounts = lottos.stream() + .map(lotto -> lotto.matchWith(winningNumbers)) + .collect(Collectors.toList()); + + return new WinningResult(matchCounts); + } +} diff --git a/src/main/java/lotto/model/Lotto.java b/src/main/java/lotto/model/Lotto.java new file mode 100644 index 000000000..28d68852d --- /dev/null +++ b/src/main/java/lotto/model/Lotto.java @@ -0,0 +1,26 @@ +package lotto.model; + +import java.util.List; + +public class Lotto { + + private final LottoNumbers numbers; + + public Lotto(List numbers) { + this.numbers = new LottoNumbers(numbers); + } + + public List getNumbers() { + return numbers.getNumbers(); + } + + public MatchCount matchWith(WinningNumbers winningNumbers) { + return new MatchCount(calculateMatchCount(winningNumbers)); + } + + private int calculateMatchCount(WinningNumbers winningNumbers) { + return (int) numbers.getNumbers().stream() + .filter(winningNumbers.getNumbers()::contains) + .count(); + } +} diff --git a/src/main/java/lotto/model/LottoGenerator.java b/src/main/java/lotto/model/LottoGenerator.java new file mode 100644 index 000000000..41c398b87 --- /dev/null +++ b/src/main/java/lotto/model/LottoGenerator.java @@ -0,0 +1,33 @@ +package lotto.model; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class LottoGenerator { + + private static final int MAX_NUMBER = 45; + private static final int LOTTO_SIZE = 6; + + public List generate(int count) { + return IntStream.range(0, count) + .mapToObj(i -> new Lotto(pickFirstSixSorted(createShuffledNumbers()))) + .collect(Collectors.toList()); + } + + private List createShuffledNumbers() { + List numbers = IntStream.rangeClosed(1, MAX_NUMBER) + .boxed() + .collect(Collectors.toList()); + Collections.shuffle(numbers); + return numbers; + } + + private List pickFirstSixSorted(List numbers) { + return numbers.stream() + .limit(LOTTO_SIZE) + .sorted() + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/lotto/model/LottoNumbers.java b/src/main/java/lotto/model/LottoNumbers.java new file mode 100644 index 000000000..a1ed52f27 --- /dev/null +++ b/src/main/java/lotto/model/LottoNumbers.java @@ -0,0 +1,26 @@ +package lotto.model; + +import java.util.Collections; +import java.util.List; + +public class LottoNumbers { + + private static final int LOTTO_SIZE = 6; + private final List numbers; + + public LottoNumbers(List numbers) { + validate(numbers); + this.numbers = numbers; + } + + private void validate(List numbers) { + if (numbers.size() != LOTTO_SIZE) { + throw new IllegalArgumentException("로또 번호는 6개여야 합니다."); + } + } + + // 수정 불가능한 숫자 리스트를 반환 + public List getNumbers() { + return Collections.unmodifiableList(numbers); + } +} diff --git a/src/main/java/lotto/model/LottoTicket.java b/src/main/java/lotto/model/LottoTicket.java new file mode 100644 index 000000000..1caf05e81 --- /dev/null +++ b/src/main/java/lotto/model/LottoTicket.java @@ -0,0 +1,14 @@ +package lotto.model; + +public class LottoTicket { + + private final int count; + + public LottoTicket(int count) { + this.count = count; + } + + public int getCount() { + return count; + } +} diff --git a/src/main/java/lotto/model/MatchCount.java b/src/main/java/lotto/model/MatchCount.java new file mode 100644 index 000000000..5b67705e8 --- /dev/null +++ b/src/main/java/lotto/model/MatchCount.java @@ -0,0 +1,14 @@ +package lotto.model; + +public class MatchCount { + + private final int count; + + public MatchCount(int count) { + this.count = count; + } + + public int getCount() { + return count; + } +} diff --git a/src/main/java/lotto/model/Money.java b/src/main/java/lotto/model/Money.java new file mode 100644 index 000000000..b7fdb3c8a --- /dev/null +++ b/src/main/java/lotto/model/Money.java @@ -0,0 +1,20 @@ +package lotto.model; + +public class Money { + + private static final int LOTTO_PRICE = 1000; + private final int value; + + public Money(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public LottoTicket calculateTicketCount() { + return new LottoTicket(value / LOTTO_PRICE); + } + +} diff --git a/src/main/java/lotto/model/WinningNumbers.java b/src/main/java/lotto/model/WinningNumbers.java new file mode 100644 index 000000000..90adfe997 --- /dev/null +++ b/src/main/java/lotto/model/WinningNumbers.java @@ -0,0 +1,17 @@ +package lotto.model; + +import java.util.List; + +public class WinningNumbers { + + private final LottoNumbers numbers; + + public WinningNumbers(List numbers) { + this.numbers = new LottoNumbers(numbers); + } + + public List getNumbers() { + return numbers.getNumbers(); + } + +} diff --git a/src/main/java/lotto/model/WinningResult.java b/src/main/java/lotto/model/WinningResult.java new file mode 100644 index 000000000..46cfefb22 --- /dev/null +++ b/src/main/java/lotto/model/WinningResult.java @@ -0,0 +1,49 @@ +package lotto.model; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class WinningResult { + + private static final Map PRIZE_MONEY = Map.of( + 3, 5_000, + 4, 50_000, + 5, 1_500_000, + 6, 2_000_000_000 + ); + + private final Map matchResults; + + public WinningResult(List matchCounts) { + this.matchResults = calculateResults(matchCounts); + } + + private Map calculateResults(List matchCounts) { + Map results = new HashMap<>(); + PRIZE_MONEY.keySet().forEach(matchCount -> results.put(matchCount, 0)); + + for (MatchCount matchCount : matchCounts) { + int count = matchCount.getCount(); + if (count >= 3) { + results.put(count, results.get(count) + 1); + } + } + + return results; + } + + public Map getWinningStatistics() { + Map statistics = new HashMap<>(); + + matchResults.forEach((matchCount, count) -> + statistics.put(String.valueOf(matchCount), (long) count)); + + long totalPrize = matchResults.entrySet().stream() + .mapToLong(entry -> (long) PRIZE_MONEY.get(entry.getKey()) * entry.getValue()) + .sum(); + + statistics.put("total", totalPrize); + return statistics; + } +} diff --git a/src/main/java/lotto/view/LottoInputView.java b/src/main/java/lotto/view/LottoInputView.java new file mode 100644 index 000000000..4751583b9 --- /dev/null +++ b/src/main/java/lotto/view/LottoInputView.java @@ -0,0 +1,30 @@ +package lotto.view; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class LottoInputView { + + public int inputMoney() { + System.out.println("구입금액을 입력해 주세요."); + Scanner scanner = new Scanner(System.in); + + return scanner.nextInt(); + } + + public List inputWinningNumber() { + System.out.println("지난 주 당첨 번호를 입력해 주세요."); + Scanner scanner = new Scanner(System.in); + + String winningNumbersString = scanner.nextLine(); + String[] numbers = winningNumbersString.split(","); + ArrayList winningNumbers = new ArrayList<>(); + + for (String num : numbers) { + winningNumbers.add(Integer.parseInt(num.trim())); + } + + return winningNumbers; + } +} diff --git a/src/main/java/lotto/view/LottoOutputView.java b/src/main/java/lotto/view/LottoOutputView.java new file mode 100644 index 000000000..99c93051c --- /dev/null +++ b/src/main/java/lotto/view/LottoOutputView.java @@ -0,0 +1,32 @@ +package lotto.view; + +import java.util.List; +import java.util.Map; +import lotto.model.Lotto; + +public class LottoOutputView { + + public void printPurchasedLottoCount(int count) { + System.out.println(count + "개를 구매했습니다."); + } + + public void printLottos(List lottos) { + for (Lotto lotto : lottos) { + System.out.println(lotto.getNumbers()); + } + } + + public void printWinningStatistics(int money, Map winningLottos) { + System.out.println("당첨 통계"); + System.out.println("---------"); + System.out.println("3개 일치 (5000원) - " + winningLottos.get("3") + "개"); + System.out.println("4개 일치 (50000원) - " + winningLottos.get("4") + "개"); + System.out.println("5개 일치 (1500000원) - " + winningLottos.get("5") + "개"); + System.out.println("6개 일치 (2000000000원) - " + winningLottos.get("6") + "개"); + + Long totalWinningMoney = winningLottos.get("total"); + + double profitRate = (double) totalWinningMoney / money; + System.out.printf("총 수익률은 %.2f%%입니다.%n", profitRate); + } +} From 82a94d1b037c6891d7f6b8c7889bb02213ef5d7a Mon Sep 17 00:00:00 2001 From: yaevrai Date: Mon, 26 May 2025 20:34:49 +0900 Subject: [PATCH 4/5] =?UTF-8?q?docs(level2):=202=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=A0=90=EA=B2=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5f382bfd3..599dd2e3f 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ ### - 요구사항 -- [ ] 로또 당첨 번호를 받아 일치한 번호 수에 따라 당첨 결과를 보여준다. +- [x] 로또 당첨 번호를 받아 일치한 번호 수에 따라 당첨 결과를 보여준다. ### - 새로운 프로그래밍 요구사항 -- [ ] 모든 원시 값과 문자열을 포장한다. -- [ ] 일급 컬렉션을 쓴다. +- [x] 모든 원시 값과 문자열을 포장한다. +- [x] 일급 컬렉션을 쓴다. ## Level3. 로또 2등 당첨 From 0f35d9b23ad6a7ba22254ea3578992591d1a08a5 Mon Sep 17 00:00:00 2001 From: yaevrai Date: Sun, 1 Jun 2025 02:00:33 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor(level2):=20PR=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/lotto/LottoService.java | 30 ++++------- src/main/java/lotto/model/Lotto.java | 26 ---------- src/main/java/lotto/model/LottoGenerator.java | 12 +++-- src/main/java/lotto/model/LottoNumbers.java | 13 +++-- src/main/java/lotto/model/LottoTicket.java | 14 ------ src/main/java/lotto/model/Lottos.java | 21 ++++++++ src/main/java/lotto/model/Money.java | 39 ++++++++++++--- src/main/java/lotto/model/PurchaseLotto.java | 33 ++++++++++++ src/main/java/lotto/model/Rank.java | 30 +++++++++++ src/main/java/lotto/model/WinningNumbers.java | 1 - src/main/java/lotto/model/WinningResult.java | 50 ++++++++----------- src/main/java/lotto/view/LottoOutputView.java | 23 +++++---- src/test/java/lotto/LottoNumbersTest.java | 32 ++++++++++++ src/test/java/lotto/MoneyTest.java | 33 ++++++++++++ src/test/java/lotto/PurchaseLottoTest.java | 23 +++++++++ 15 files changed, 265 insertions(+), 115 deletions(-) delete mode 100644 src/main/java/lotto/model/Lotto.java delete mode 100644 src/main/java/lotto/model/LottoTicket.java create mode 100644 src/main/java/lotto/model/Lottos.java create mode 100644 src/main/java/lotto/model/PurchaseLotto.java create mode 100644 src/main/java/lotto/model/Rank.java create mode 100644 src/test/java/lotto/LottoNumbersTest.java create mode 100644 src/test/java/lotto/MoneyTest.java create mode 100644 src/test/java/lotto/PurchaseLottoTest.java diff --git a/src/main/java/lotto/LottoService.java b/src/main/java/lotto/LottoService.java index 7636edd4b..232651920 100644 --- a/src/main/java/lotto/LottoService.java +++ b/src/main/java/lotto/LottoService.java @@ -2,11 +2,11 @@ import java.util.List; import java.util.stream.Collectors; -import lotto.model.Lotto; import lotto.model.LottoGenerator; -import lotto.model.LottoTicket; +import lotto.model.Lottos; import lotto.model.MatchCount; import lotto.model.Money; +import lotto.model.PurchaseLotto; import lotto.model.WinningNumbers; import lotto.model.WinningResult; import lotto.view.LottoInputView; @@ -25,30 +25,22 @@ public LottoService(LottoInputView inputView, LottoOutputView outputView) { } public void start() { - Money purchaseMoney = new Money(inputView.inputMoney()); + Money money = new Money(inputView.inputMoney()); + PurchaseLotto purchaseLotto = new PurchaseLotto(money, lottoGenerator); + Lottos lottos = purchaseLotto.getLottos(); - List lottos = purchaseLottos(purchaseMoney); - - LottoTicket lottoTicket = purchaseMoney.calculateTicketCount(); - - outputView.printPurchasedLottoCount(lottoTicket.getCount()); - outputView.printLottos(lottos); + outputView.printPurchasedLottoCount(purchaseLotto.purchaseCount()); + outputView.printLottos(lottos.asList()); WinningNumbers winningNumbers = new WinningNumbers(inputView.inputWinningNumber()); - WinningResult winningResult = calculateWinningResult(lottos, winningNumbers); - outputView.printWinningStatistics(purchaseMoney.getValue(), - winningResult.getWinningStatistics()); - } - - private List purchaseLottos(Money money) { - return lottoGenerator.generate(money.calculateTicketCount().getCount()); + outputView.printWinningStatistics(money.getAmount(), winningResult.getWinningStatistics()); } - private WinningResult calculateWinningResult(List lottos, WinningNumbers winningNumbers) { - List matchCounts = lottos.stream() - .map(lotto -> lotto.matchWith(winningNumbers)) + private WinningResult calculateWinningResult(Lottos lottos, WinningNumbers winningNumbers) { + List matchCounts = lottos.asList().stream() + .map(lotto -> lotto.match(winningNumbers)) .collect(Collectors.toList()); return new WinningResult(matchCounts); diff --git a/src/main/java/lotto/model/Lotto.java b/src/main/java/lotto/model/Lotto.java deleted file mode 100644 index 28d68852d..000000000 --- a/src/main/java/lotto/model/Lotto.java +++ /dev/null @@ -1,26 +0,0 @@ -package lotto.model; - -import java.util.List; - -public class Lotto { - - private final LottoNumbers numbers; - - public Lotto(List numbers) { - this.numbers = new LottoNumbers(numbers); - } - - public List getNumbers() { - return numbers.getNumbers(); - } - - public MatchCount matchWith(WinningNumbers winningNumbers) { - return new MatchCount(calculateMatchCount(winningNumbers)); - } - - private int calculateMatchCount(WinningNumbers winningNumbers) { - return (int) numbers.getNumbers().stream() - .filter(winningNumbers.getNumbers()::contains) - .count(); - } -} diff --git a/src/main/java/lotto/model/LottoGenerator.java b/src/main/java/lotto/model/LottoGenerator.java index 41c398b87..c94061489 100644 --- a/src/main/java/lotto/model/LottoGenerator.java +++ b/src/main/java/lotto/model/LottoGenerator.java @@ -10,24 +10,26 @@ public class LottoGenerator { private static final int MAX_NUMBER = 45; private static final int LOTTO_SIZE = 6; - public List generate(int count) { - return IntStream.range(0, count) - .mapToObj(i -> new Lotto(pickFirstSixSorted(createShuffledNumbers()))) - .collect(Collectors.toList()); + public LottoNumbers generate() { + List shuffledNumbers = createShuffledNumbers(); + List pickSixNumber = pickRandomSixSorted(shuffledNumbers); + return new LottoNumbers(pickSixNumber); } private List createShuffledNumbers() { List numbers = IntStream.rangeClosed(1, MAX_NUMBER) .boxed() .collect(Collectors.toList()); + Collections.shuffle(numbers); return numbers; } - private List pickFirstSixSorted(List numbers) { + private List pickRandomSixSorted(List numbers) { return numbers.stream() .limit(LOTTO_SIZE) .sorted() .collect(Collectors.toList()); } } + diff --git a/src/main/java/lotto/model/LottoNumbers.java b/src/main/java/lotto/model/LottoNumbers.java index a1ed52f27..7655f4b21 100644 --- a/src/main/java/lotto/model/LottoNumbers.java +++ b/src/main/java/lotto/model/LottoNumbers.java @@ -1,6 +1,5 @@ package lotto.model; -import java.util.Collections; import java.util.List; public class LottoNumbers { @@ -10,7 +9,7 @@ public class LottoNumbers { public LottoNumbers(List numbers) { validate(numbers); - this.numbers = numbers; + this.numbers = List.copyOf(numbers); } private void validate(List numbers) { @@ -19,8 +18,14 @@ private void validate(List numbers) { } } - // 수정 불가능한 숫자 리스트를 반환 + public MatchCount match(WinningNumbers winningNumbers) { + long count = numbers.stream() + .filter(winningNumbers.getNumbers()::contains) + .count(); + return new MatchCount((int) count); + } + public List getNumbers() { - return Collections.unmodifiableList(numbers); + return numbers; } } diff --git a/src/main/java/lotto/model/LottoTicket.java b/src/main/java/lotto/model/LottoTicket.java deleted file mode 100644 index 1caf05e81..000000000 --- a/src/main/java/lotto/model/LottoTicket.java +++ /dev/null @@ -1,14 +0,0 @@ -package lotto.model; - -public class LottoTicket { - - private final int count; - - public LottoTicket(int count) { - this.count = count; - } - - public int getCount() { - return count; - } -} diff --git a/src/main/java/lotto/model/Lottos.java b/src/main/java/lotto/model/Lottos.java new file mode 100644 index 000000000..56351ee16 --- /dev/null +++ b/src/main/java/lotto/model/Lottos.java @@ -0,0 +1,21 @@ +package lotto.model; + +import java.util.List; + +public class Lottos { + + private final List lottoNumbers; + + public Lottos(List lottoNumbers) { + this.lottoNumbers = List.copyOf(lottoNumbers); + } + + public List asList() { + return lottoNumbers; + } + + public int size() { + return lottoNumbers.size(); + } + +} diff --git a/src/main/java/lotto/model/Money.java b/src/main/java/lotto/model/Money.java index b7fdb3c8a..e32429486 100644 --- a/src/main/java/lotto/model/Money.java +++ b/src/main/java/lotto/model/Money.java @@ -1,20 +1,43 @@ package lotto.model; +import java.util.Objects; + public class Money { - private static final int LOTTO_PRICE = 1000; - private final int value; + private final int amount; + + public Money(int amount) { + validate(amount); + this.amount = amount; + } - public Money(int value) { - this.value = value; + public int getAmount() { + return amount; } - public int getValue() { - return value; + private void validate(int amount) { + if (amount < 0) { + throw new IllegalArgumentException("금액은 음수일 수 없습니다."); + } + if (amount % 1000 != 0) { + throw new IllegalArgumentException("금액은 1000원 단위여야 합니다."); + } } - public LottoTicket calculateTicketCount() { - return new LottoTicket(value / LOTTO_PRICE); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Money money = (Money) o; + return amount == money.amount; } + @Override + public int hashCode() { + return Objects.hash(this.amount); + } } diff --git a/src/main/java/lotto/model/PurchaseLotto.java b/src/main/java/lotto/model/PurchaseLotto.java new file mode 100644 index 000000000..31eda7474 --- /dev/null +++ b/src/main/java/lotto/model/PurchaseLotto.java @@ -0,0 +1,33 @@ +package lotto.model; + +import java.util.List; +import java.util.Objects; + +public class PurchaseLotto { + + public static final int PRICE = 1000; + private final Money money; + private final Lottos lottos; + + public PurchaseLotto(Money money, LottoGenerator lottoGenerator) { + Objects.requireNonNull(money); + Objects.requireNonNull(lottoGenerator); + + this.money = money; + this.lottos = new Lottos(generateLottos(lottoGenerator, purchaseCount())); + } + + public int purchaseCount() { + return money.getAmount() / PRICE; + } + + public Lottos getLottos() { + return lottos; + } + + private List generateLottos(LottoGenerator generator, int count) { + return java.util.stream.IntStream.range(0, count) + .mapToObj(i -> generator.generate()) + .collect(java.util.stream.Collectors.toList()); + } +} diff --git a/src/main/java/lotto/model/Rank.java b/src/main/java/lotto/model/Rank.java new file mode 100644 index 000000000..d223721b1 --- /dev/null +++ b/src/main/java/lotto/model/Rank.java @@ -0,0 +1,30 @@ +package lotto.model; + +public enum Rank { + MATCH_3(3, 5_000, "3개 일치 (5000원)"), + MATCH_4(4, 50_000, "4개 일치 (50000원)"), + MATCH_5(5, 1_500_000, "5개 일치 (1500000원)"), + MATCH_6(6, 2_000_000_000, "6개 일치 (2000000000원)"); + + private final int matchCount; + private final int prize; + private final String display; + + Rank(int matchCount, int prize, String display) { + this.matchCount = matchCount; + this.prize = prize; + this.display = display; + } + + public int getMatchCount() { + return matchCount; + } + + public int getPrize() { + return prize; + } + + public String getDisplay() { + return display; + } +} diff --git a/src/main/java/lotto/model/WinningNumbers.java b/src/main/java/lotto/model/WinningNumbers.java index 90adfe997..83cc3f955 100644 --- a/src/main/java/lotto/model/WinningNumbers.java +++ b/src/main/java/lotto/model/WinningNumbers.java @@ -13,5 +13,4 @@ public WinningNumbers(List numbers) { public List getNumbers() { return numbers.getNumbers(); } - } diff --git a/src/main/java/lotto/model/WinningResult.java b/src/main/java/lotto/model/WinningResult.java index 46cfefb22..b378b7d93 100644 --- a/src/main/java/lotto/model/WinningResult.java +++ b/src/main/java/lotto/model/WinningResult.java @@ -1,49 +1,43 @@ package lotto.model; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Objects; public class WinningResult { - private static final Map PRIZE_MONEY = Map.of( - 3, 5_000, - 4, 50_000, - 5, 1_500_000, - 6, 2_000_000_000 - ); - - private final Map matchResults; + private final Map matchResults; public WinningResult(List matchCounts) { this.matchResults = calculateResults(matchCounts); } - private Map calculateResults(List matchCounts) { - Map results = new HashMap<>(); - PRIZE_MONEY.keySet().forEach(matchCount -> results.put(matchCount, 0)); + private Map calculateResults(List matchCounts) { + Map results = new EnumMap<>(Rank.class); - for (MatchCount matchCount : matchCounts) { - int count = matchCount.getCount(); - if (count >= 3) { - results.put(count, results.get(count) + 1); - } + for (Rank rank : Rank.values()) { + results.put(rank, 0L); } + matchCounts.stream() + .map(mc -> findRankByMatchCount(mc.getCount())) + .filter(Objects::nonNull) + .forEach(rank -> results.put(rank, results.get(rank) + 1)); + return results; } - public Map getWinningStatistics() { - Map statistics = new HashMap<>(); - - matchResults.forEach((matchCount, count) -> - statistics.put(String.valueOf(matchCount), (long) count)); - - long totalPrize = matchResults.entrySet().stream() - .mapToLong(entry -> (long) PRIZE_MONEY.get(entry.getKey()) * entry.getValue()) - .sum(); + private Rank findRankByMatchCount(int matchCount) { + for (Rank rank : Rank.values()) { + if (rank.getMatchCount() == matchCount) { + return rank; + } + } + return null; + } - statistics.put("total", totalPrize); - return statistics; + public Map getWinningStatistics() { + return new EnumMap<>(matchResults); } } diff --git a/src/main/java/lotto/view/LottoOutputView.java b/src/main/java/lotto/view/LottoOutputView.java index 99c93051c..91067eb22 100644 --- a/src/main/java/lotto/view/LottoOutputView.java +++ b/src/main/java/lotto/view/LottoOutputView.java @@ -2,7 +2,8 @@ import java.util.List; import java.util.Map; -import lotto.model.Lotto; +import lotto.model.LottoNumbers; +import lotto.model.Rank; public class LottoOutputView { @@ -10,23 +11,25 @@ public void printPurchasedLottoCount(int count) { System.out.println(count + "개를 구매했습니다."); } - public void printLottos(List lottos) { - for (Lotto lotto : lottos) { + public void printLottos(List lottoNumbers) { + for (LottoNumbers lotto : lottoNumbers) { System.out.println(lotto.getNumbers()); } } - public void printWinningStatistics(int money, Map winningLottos) { + public void printWinningStatistics(int money, Map winningLottos) { System.out.println("당첨 통계"); System.out.println("---------"); - System.out.println("3개 일치 (5000원) - " + winningLottos.get("3") + "개"); - System.out.println("4개 일치 (50000원) - " + winningLottos.get("4") + "개"); - System.out.println("5개 일치 (1500000원) - " + winningLottos.get("5") + "개"); - System.out.println("6개 일치 (2000000000원) - " + winningLottos.get("6") + "개"); + long totalPrize = 0L; - Long totalWinningMoney = winningLottos.get("total"); + for (Rank rank : Rank.values()) { + long count = winningLottos.getOrDefault(rank, 0L); + System.out.println(rank.getDisplay() + " - " + count + "개"); + totalPrize += count * rank.getPrize(); + } - double profitRate = (double) totalWinningMoney / money; + double profitRate = (double) totalPrize / money; System.out.printf("총 수익률은 %.2f%%입니다.%n", profitRate); + } } diff --git a/src/test/java/lotto/LottoNumbersTest.java b/src/test/java/lotto/LottoNumbersTest.java new file mode 100644 index 000000000..53422fe7c --- /dev/null +++ b/src/test/java/lotto/LottoNumbersTest.java @@ -0,0 +1,32 @@ +package lotto; + +import lotto.model.LottoNumbers; +import lotto.model.MatchCount; +import lotto.model.WinningNumbers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class LottoNumbersTest { + + @Test + @DisplayName("로또 번호가 6개가 아니면 예외를 발생") + void throwExceptionIfLottoNumbersAreNotSix() { + assertThrows(IllegalArgumentException.class, () -> + new LottoNumbers(Arrays.asList(1, 2, 3, 4, 5)) + ); + } + + @Test + @DisplayName("로또 번호와 당첨 번호를 비교해 일치 개수를 계산") + void calculateMatchedCountCorrectly() { + LottoNumbers lotto = new LottoNumbers(Arrays.asList(1, 2, 3, 4, 5, 6)); + WinningNumbers winning = new WinningNumbers(Arrays.asList(1, 2, 3, 7, 8, 9)); + + MatchCount result = lotto.match(winning); + assertThat(result.getCount()).isEqualTo(3); + } +} diff --git a/src/test/java/lotto/MoneyTest.java b/src/test/java/lotto/MoneyTest.java new file mode 100644 index 000000000..efd3174fb --- /dev/null +++ b/src/test/java/lotto/MoneyTest.java @@ -0,0 +1,33 @@ +package lotto; + +import lotto.model.Money; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class MoneyTest { + + @Test + @DisplayName("음수 금액 입력 시 예외 발생") + void throwExceptionIfAmountIsNegative() { + assertThrows(IllegalArgumentException.class, () -> new Money(-1000)); + } + + @Test + @DisplayName("1000원 단위가 아니면 예외 발생") + void throwExceptionIfAmountIsNotThousandUnit() { + assertThrows(IllegalArgumentException.class, () -> new Money(1500)); + } + + @Test + @DisplayName("VO 객체가 아니면 예외 발생") + public void voCheck() { + Money first = new Money(1000); + Money second = new Money(1000); + + assertThat(first).isEqualTo(second); + assertThat(first.hashCode()).isEqualTo(second.hashCode()); + } +} diff --git a/src/test/java/lotto/PurchaseLottoTest.java b/src/test/java/lotto/PurchaseLottoTest.java new file mode 100644 index 000000000..8dc8b5b8f --- /dev/null +++ b/src/test/java/lotto/PurchaseLottoTest.java @@ -0,0 +1,23 @@ +package lotto; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import lotto.model.LottoGenerator; +import lotto.model.Money; +import lotto.model.PurchaseLotto; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PurchaseLottoTest { + + @Test + @DisplayName("금액에 맞는 로또 개수인지 확인") + void calculateLottoCountByAmount() { + Money money = new Money(5000); + PurchaseLotto purchase = new PurchaseLotto(money, new LottoGenerator()); + + assertThat(purchase.purchaseCount()).isEqualTo(5); + assertThat(purchase.getLottos().size()).isEqualTo(5); + } + +}