From e37e5862ea33afca981bf6dd71e0302848d87131 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Fri, 31 Jul 2026 22:00:28 +0900 Subject: [PATCH 01/28] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=83=9D=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 41 +++++++++++++++++++++++++++++ src/test/java/domain/LottoTest.java | 25 ++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/domain/Lotto.java create mode 100644 src/test/java/domain/LottoTest.java diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java new file mode 100644 index 000000000..c4800b5d5 --- /dev/null +++ b/src/main/java/domain/Lotto.java @@ -0,0 +1,41 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class Lotto { + private static final int NUMBER_COUNT = 6; + private static final int RANDOM_NUMBER_BOUND = 45; + + private final Random random = new Random(); + private final List numbers = new ArrayList<>(NUMBER_COUNT); + + public Lotto() { + makeNumbers(); + } + + public List getNumbers() { + return List.copyOf(numbers); + } + + private void makeNumbers() { + for (int i = 0; i < NUMBER_COUNT; i++) { + numbers.add(generateRandomNumber()); + } + } + + private int generateRandomNumber() { + int number; + + do { + number = random.nextInt(RANDOM_NUMBER_BOUND) + 1; + } while (containsNumber(number)); + + return number; + } + + private boolean containsNumber(int number) { + return numbers.contains(number); + } +} diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java new file mode 100644 index 000000000..6969c9a14 --- /dev/null +++ b/src/test/java/domain/LottoTest.java @@ -0,0 +1,25 @@ +package domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LottoTest { + @Test + @DisplayName("로또 번호는 중복되지 않는다") + void lottoNumbersShouldNotContainsDuplicates() { + Lotto lotto = new Lotto(); + + assertThat(lotto.getNumbers()).doesNotHaveDuplicates(); + } + + @Test + @DisplayName("로또 번호는 1부터 45 사이이다") + void lottoNumbersShouldBeBetweenOneAndFortyFive() { + Lotto lotto = new Lotto(); + + assertThat(lotto.getNumbers()) + .allMatch(number -> number >= 1 && number <= 45); + } +} From 1aaeca7cf2bd86055124cf14dafb531c923311f6 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Fri, 31 Jul 2026 22:56:53 +0900 Subject: [PATCH 02/28] =?UTF-8?q?feat:=20=EA=B5=AC=EC=9E=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EB=A1=9C=EB=98=90?= =?UTF-8?q?=EB=A5=BC=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8A=94=20Lottos=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lottos.java | 32 ++++++++++++++++++++++++ src/test/java/domain/LottosTest.java | 37 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/main/java/domain/Lottos.java create mode 100644 src/test/java/domain/LottosTest.java diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java new file mode 100644 index 000000000..b46cf5917 --- /dev/null +++ b/src/main/java/domain/Lottos.java @@ -0,0 +1,32 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; + +public class Lottos { + private static final int LOTTO_PRICE = 1000; + + private final List lottos = new ArrayList<>(); + + private int lottoCount; + + public Lottos(int amount) { + if (amount < LOTTO_PRICE) { + throw new IllegalArgumentException("최소 구입 금액은 1000원 입니다."); + } + + calculateLottoCount(amount); + + for (int i = 0; i < lottoCount; i++) { + lottos.add(new Lotto()); + } + } + + public List getLottos() { + return List.copyOf(lottos); + } + + private void calculateLottoCount(int amount) { + lottoCount = amount / LOTTO_PRICE; + } +} diff --git a/src/test/java/domain/LottosTest.java b/src/test/java/domain/LottosTest.java new file mode 100644 index 000000000..6bcb41c07 --- /dev/null +++ b/src/test/java/domain/LottosTest.java @@ -0,0 +1,37 @@ +package domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class LottosTest { + @Test + @DisplayName("구입 금액에 해당하는 개수만큼 로또를 생성한다") + void createsLottosBasedOnPurchaseAmount() { + Lottos lottos = new Lottos(14000); + + assertEquals(14, lottos.getLottos().size()); + } + + @Test + @DisplayName("최소 구입 금액으로도 로또 한 장을 생성한다") + void createsOneLottoForMinimumPurchaseAmount() { + Lottos lottos = new Lottos(1000); + + assertEquals(1, lottos.getLottos().size()); + } + + @ParameterizedTest + @ValueSource(ints = {999, 0, -5000}) + @DisplayName("구입 금액이 최소 구입 금액보다 작으면 예외가 발생한다") + void throwsExceptionWhenPurchaseAmountIsLessThanLottoPrice(int amount) { + assertThrows( + IllegalArgumentException.class, + () -> new Lottos(amount) + ); + } +} From 9fa4cfd8e4c027a850f659bba686edf9026cc1ca Mon Sep 17 00:00:00 2001 From: johaeunn Date: Fri, 31 Jul 2026 23:00:14 +0900 Subject: [PATCH 03/28] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=A0=95=EB=A0=AC=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index c4800b5d5..c617729a0 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -1,6 +1,7 @@ package domain; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; @@ -9,7 +10,7 @@ public class Lotto { private static final int RANDOM_NUMBER_BOUND = 45; private final Random random = new Random(); - private final List numbers = new ArrayList<>(NUMBER_COUNT); + private final List numbers = new ArrayList<>(); public Lotto() { makeNumbers(); @@ -23,6 +24,8 @@ private void makeNumbers() { for (int i = 0; i < NUMBER_COUNT; i++) { numbers.add(generateRandomNumber()); } + + Collections.sort(numbers); } private int generateRandomNumber() { From 488dc15fdd48a787072dcf0e0546c49ddfc6a90f Mon Sep 17 00:00:00 2001 From: johaeunn Date: Fri, 31 Jul 2026 23:41:38 +0900 Subject: [PATCH 04/28] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EC=9E=85?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lottos.java | 4 ++++ src/main/java/view/InputView.java | 15 +++++++++++++++ src/main/java/view/OutputView.java | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index b46cf5917..090c0e910 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -26,6 +26,10 @@ public List getLottos() { return List.copyOf(lottos); } + public int getLottoCount() { + return lottoCount; + } + private void calculateLottoCount(int amount) { lottoCount = amount / LOTTO_PRICE; } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 000000000..76bf7f93b --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,15 @@ +package view; + +import java.util.Scanner; + +public class InputView { + private final Scanner scanner = new Scanner(System.in); + + public int readPurchaseAmount() { + System.out.println("구입금액을 입력해 주세요."); + int amount = scanner.nextInt(); + System.out.println(); + + return amount; + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 000000000..ec6bf4a7c --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,16 @@ +package view; + +import domain.Lotto; +import domain.Lottos; + +public class OutputView { + public void printPurchasedCount(Lottos lottos) { + System.out.println(lottos.getLottoCount() + "개를 구매했습니다."); + } + + public void printLottos(Lottos lottos) { + for (Lotto lotto : lottos.getLottos()) { + System.out.println(lotto.getNumbers()); + } + } +} From ce2dd97988fb009f3f5181eb2e55dba9b820910e Mon Sep 17 00:00:00 2001 From: johaeunn Date: Fri, 31 Jul 2026 23:41:53 +0900 Subject: [PATCH 05/28] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EC=95=A0?= =?UTF-8?q?=ED=94=8C=EB=A6=AC=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=20=ED=9D=90=EB=A6=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/Application.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 000000000..2ea63b879 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,17 @@ +import domain.Lottos; +import view.InputView; +import view.OutputView; + +public class Application { + public static void main(String[] args) { + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + + int amount = inputView.readPurchaseAmount(); + + Lottos lottos = new Lottos(amount); + + outputView.printPurchasedCount(lottos); + outputView.printLottos(lottos); + } +} From 237b079b8baf798af72dc5ac1edd2fc951a1c0b1 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sat, 1 Aug 2026 00:47:56 +0900 Subject: [PATCH 06/28] =?UTF-8?q?refactor:=20=EA=B5=AC=EB=A7=A4=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=EC=9D=84=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20?= =?UTF-8?q?=ED=8F=AC=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 8 +++-- src/main/java/domain/Lottos.java | 20 ++--------- src/main/java/domain/PurchaseAmount.java | 19 ++++++++++ src/main/java/view/InputView.java | 4 +-- src/main/java/view/OutputView.java | 5 +-- src/test/java/domain/LottosTest.java | 26 ++------------ src/test/java/domain/PurchaseAmountTest.java | 37 ++++++++++++++++++++ 7 files changed, 71 insertions(+), 48 deletions(-) create mode 100644 src/main/java/domain/PurchaseAmount.java create mode 100644 src/test/java/domain/PurchaseAmountTest.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 2ea63b879..05bf2751e 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,4 +1,5 @@ import domain.Lottos; +import domain.PurchaseAmount; import view.InputView; import view.OutputView; @@ -7,11 +8,12 @@ public static void main(String[] args) { InputView inputView = new InputView(); OutputView outputView = new OutputView(); - int amount = inputView.readPurchaseAmount(); + int inputAmount = inputView.readPurchaseAmount(); + PurchaseAmount purchaseAmount = new PurchaseAmount(inputAmount); - Lottos lottos = new Lottos(amount); + Lottos lottos = new Lottos(purchaseAmount); - outputView.printPurchasedCount(lottos); + outputView.printPurchasedCount(purchaseAmount); outputView.printLottos(lottos); } } diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 090c0e910..59a9a5b96 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -4,18 +4,10 @@ import java.util.List; public class Lottos { - private static final int LOTTO_PRICE = 1000; - private final List lottos = new ArrayList<>(); - private int lottoCount; - - public Lottos(int amount) { - if (amount < LOTTO_PRICE) { - throw new IllegalArgumentException("최소 구입 금액은 1000원 입니다."); - } - - calculateLottoCount(amount); + public Lottos(PurchaseAmount purchaseAmount) { + int lottoCount = purchaseAmount.calculateLottoCount(); for (int i = 0; i < lottoCount; i++) { lottos.add(new Lotto()); @@ -25,12 +17,4 @@ public Lottos(int amount) { public List getLottos() { return List.copyOf(lottos); } - - public int getLottoCount() { - return lottoCount; - } - - private void calculateLottoCount(int amount) { - lottoCount = amount / LOTTO_PRICE; - } } diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java new file mode 100644 index 000000000..f83f71c3e --- /dev/null +++ b/src/main/java/domain/PurchaseAmount.java @@ -0,0 +1,19 @@ +package domain; + +public class PurchaseAmount { + private static final int LOTTO_PRICE = 1000; + + private final int amount; + + public PurchaseAmount(int amount) { + if (amount < LOTTO_PRICE) { + throw new IllegalArgumentException("최소 구입 금액은 1000원 입니다."); + } + + this.amount = amount; + } + + public int calculateLottoCount() { + return amount / LOTTO_PRICE; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 76bf7f93b..799c28a2f 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -7,9 +7,9 @@ public class InputView { public int readPurchaseAmount() { System.out.println("구입금액을 입력해 주세요."); - int amount = scanner.nextInt(); + int purchaseAmount = scanner.nextInt(); System.out.println(); - return amount; + return purchaseAmount; } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index ec6bf4a7c..9e3b55444 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -2,10 +2,11 @@ import domain.Lotto; import domain.Lottos; +import domain.PurchaseAmount; public class OutputView { - public void printPurchasedCount(Lottos lottos) { - System.out.println(lottos.getLottoCount() + "개를 구매했습니다."); + public void printPurchasedCount(PurchaseAmount purchaseAmount) { + System.out.println(purchaseAmount.calculateLottoCount() + "개를 구매했습니다."); } public void printLottos(Lottos lottos) { diff --git a/src/test/java/domain/LottosTest.java b/src/test/java/domain/LottosTest.java index 6bcb41c07..9db5d0258 100644 --- a/src/test/java/domain/LottosTest.java +++ b/src/test/java/domain/LottosTest.java @@ -1,37 +1,17 @@ package domain; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; public class LottosTest { @Test - @DisplayName("구입 금액에 해당하는 개수만큼 로또를 생성한다") + @DisplayName("구입 금액으로 계산된 개수만큼 로또를 생성한다") void createsLottosBasedOnPurchaseAmount() { - Lottos lottos = new Lottos(14000); + PurchaseAmount purchaseAmount = new PurchaseAmount(14000); + Lottos lottos = new Lottos(purchaseAmount); assertEquals(14, lottos.getLottos().size()); } - - @Test - @DisplayName("최소 구입 금액으로도 로또 한 장을 생성한다") - void createsOneLottoForMinimumPurchaseAmount() { - Lottos lottos = new Lottos(1000); - - assertEquals(1, lottos.getLottos().size()); - } - - @ParameterizedTest - @ValueSource(ints = {999, 0, -5000}) - @DisplayName("구입 금액이 최소 구입 금액보다 작으면 예외가 발생한다") - void throwsExceptionWhenPurchaseAmountIsLessThanLottoPrice(int amount) { - assertThrows( - IllegalArgumentException.class, - () -> new Lottos(amount) - ); - } } diff --git a/src/test/java/domain/PurchaseAmountTest.java b/src/test/java/domain/PurchaseAmountTest.java new file mode 100644 index 000000000..a6f0622cb --- /dev/null +++ b/src/test/java/domain/PurchaseAmountTest.java @@ -0,0 +1,37 @@ +package domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class PurchaseAmountTest { + @Test + @DisplayName("구입 금액에 따라 구매 가능한 로또 개수를 계산한다") + void calculatesLottoCountBasedOnPurchaseAmount() { + PurchaseAmount purchaseAmount = new PurchaseAmount(5000); + + assertEquals(5, purchaseAmount.calculateLottoCount()); + } + + @Test + @DisplayName("최소 구입 금액으로 구매 가능한 로또 개수는 1개이다") + void calculatesOneLottoForMinimumPurchaseAmount() { + PurchaseAmount purchaseAmount = new PurchaseAmount(1000); + + assertEquals(1, purchaseAmount.calculateLottoCount()); + } + + @ParameterizedTest + @ValueSource(ints = {999, 0, -5000}) + @DisplayName("구입 금액이 최소 구입 금액보다 작으면 예외가 발생한다") + void throwsExceptionWhenPurchaseAmountIsLessThanLottoPrice(int amount) { + assertThrows( + IllegalArgumentException.class, + () -> new PurchaseAmount(amount) + ); + } +} From 0f179869367cf838cffa5e23fc6ad4764cc4516f Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sat, 1 Aug 2026 03:48:48 +0900 Subject: [PATCH 07/28] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EB=A5=BC=20=EA=B0=9D=EC=B2=B4=EB=A1=9C=20?= =?UTF-8?q?=ED=8F=AC=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 18 ++++----- src/main/java/domain/LottoNumber.java | 47 +++++++++++++++++++++++ src/main/java/view/OutputView.java | 16 +++++++- src/test/java/domain/LottoNumberTest.java | 29 ++++++++++++++ src/test/java/domain/LottoTest.java | 11 +++++- 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 src/main/java/domain/LottoNumber.java create mode 100644 src/test/java/domain/LottoNumberTest.java diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index c617729a0..363bb676e 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -10,13 +10,13 @@ public class Lotto { private static final int RANDOM_NUMBER_BOUND = 45; private final Random random = new Random(); - private final List numbers = new ArrayList<>(); + private final List numbers = new ArrayList<>(); public Lotto() { makeNumbers(); } - public List getNumbers() { + public List getNumbers() { return List.copyOf(numbers); } @@ -28,17 +28,17 @@ private void makeNumbers() { Collections.sort(numbers); } - private int generateRandomNumber() { - int number; + private LottoNumber generateRandomNumber() { + LottoNumber lottoNumber; do { - number = random.nextInt(RANDOM_NUMBER_BOUND) + 1; - } while (containsNumber(number)); + lottoNumber = new LottoNumber(random.nextInt(RANDOM_NUMBER_BOUND) + 1); + } while (containsNumber(lottoNumber)); - return number; + return lottoNumber; } - private boolean containsNumber(int number) { - return numbers.contains(number); + private boolean containsNumber(LottoNumber lottoNumber) { + return numbers.contains(lottoNumber); } } diff --git a/src/main/java/domain/LottoNumber.java b/src/main/java/domain/LottoNumber.java new file mode 100644 index 000000000..a2013d87d --- /dev/null +++ b/src/main/java/domain/LottoNumber.java @@ -0,0 +1,47 @@ +package domain; + +import java.util.Objects; + +public class LottoNumber implements Comparable{ + private static final int MIN_LOTTO_NUMBER = 1; + private static final int MAX_LOTTO_NUMBER = 45; + + private final int number; + + public LottoNumber(int number) { + if (number < MIN_LOTTO_NUMBER || number > MAX_LOTTO_NUMBER) { + throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + + this.number = number; + } + + public int getNumber() { + return number; + } + + @Override + public int compareTo(LottoNumber lottoNumber) { + return Integer.compare(this.number, lottoNumber.number); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof LottoNumber)) { + return false; + } + + LottoNumber lottoNumber = (LottoNumber) object; + + return this.number == lottoNumber.number; + } + + @Override + public int hashCode() { + return Objects.hash(number); + } +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 9e3b55444..a84a911e0 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,9 +1,13 @@ package view; import domain.Lotto; +import domain.LottoNumber; import domain.Lottos; import domain.PurchaseAmount; +import java.util.ArrayList; +import java.util.List; + public class OutputView { public void printPurchasedCount(PurchaseAmount purchaseAmount) { System.out.println(purchaseAmount.calculateLottoCount() + "개를 구매했습니다."); @@ -11,7 +15,17 @@ public void printPurchasedCount(PurchaseAmount purchaseAmount) { public void printLottos(Lottos lottos) { for (Lotto lotto : lottos.getLottos()) { - System.out.println(lotto.getNumbers()); + printLotto(lotto); + } + } + + private void printLotto(Lotto lotto) { + List numberValues = new ArrayList<>(); + + for (LottoNumber lottoNumber : lotto.getNumbers()) { + numberValues.add(lottoNumber.getNumber()); } + + System.out.println(numberValues); } } diff --git a/src/test/java/domain/LottoNumberTest.java b/src/test/java/domain/LottoNumberTest.java new file mode 100644 index 000000000..aa77e06cb --- /dev/null +++ b/src/test/java/domain/LottoNumberTest.java @@ -0,0 +1,29 @@ +package domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class LottoNumberTest { + @ParameterizedTest + @ValueSource(ints = {1, 45}) + @DisplayName("로또 번호가 1 이상 45 이하이면 생성할 수 있다") + void createLottoNumberWithinValidRange(int value) { + LottoNumber lottoNumber = new LottoNumber(value); + + assertEquals(value, lottoNumber.getNumber()); + } + + @ParameterizedTest + @ValueSource(ints = {0, 46}) + @DisplayName("로또 번호가 유효한 범위를 벗어나면 예외가 발생한다") + void throwsExceptionWhenLottoNumberIsOutOfRange(int value) { + assertThrows( + IllegalArgumentException.class, + () -> new LottoNumber(value) + ); + } +} diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java index 6969c9a14..353961131 100644 --- a/src/test/java/domain/LottoTest.java +++ b/src/test/java/domain/LottoTest.java @@ -20,6 +20,15 @@ void lottoNumbersShouldBeBetweenOneAndFortyFive() { Lotto lotto = new Lotto(); assertThat(lotto.getNumbers()) - .allMatch(number -> number >= 1 && number <= 45); + .allMatch(lottoNumber -> lottoNumber.getNumber() >= 1 + && lottoNumber.getNumber() <= 45); + } + + @Test + @DisplayName("로또는 6개의 번호를 가진다") + void lottoShouldContainsSizNumbers() { + Lotto lotto = new Lotto(); + + assertThat(lotto.getNumbers()).hasSize(6); } } From a64e5db8d7e90a52c4b7d0ab40f736cc03cd0463 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sat, 1 Aug 2026 06:12:30 +0900 Subject: [PATCH 08/28] =?UTF-8?q?feat:=20=EB=8B=B9=EC=B2=A8=20=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B2=80=EC=A6=9D=20=EB=B0=8F=20=EB=8B=B9=EC=B2=A8?= =?UTF-8?q?=20=ED=86=B5=EA=B3=84=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 8 +++ src/main/java/domain/LottoStatistics.java | 64 ++++++++++++++++++++ src/main/java/domain/PurchaseAmount.java | 4 ++ src/main/java/domain/WinningNumbers.java | 57 +++++++++++++++++ src/main/java/view/InputView.java | 23 ++++++- src/main/java/view/OutputView.java | 35 +++++++++-- src/test/java/domain/WinningNumbersTest.java | 39 ++++++++++++ 7 files changed, 225 insertions(+), 5 deletions(-) create mode 100644 src/main/java/domain/LottoStatistics.java create mode 100644 src/main/java/domain/WinningNumbers.java create mode 100644 src/test/java/domain/WinningNumbersTest.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 05bf2751e..cfa982a8c 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,8 +1,11 @@ import domain.Lottos; import domain.PurchaseAmount; +import domain.WinningNumbers; import view.InputView; import view.OutputView; +import java.util.List; + public class Application { public static void main(String[] args) { InputView inputView = new InputView(); @@ -15,5 +18,10 @@ public static void main(String[] args) { outputView.printPurchasedCount(purchaseAmount); outputView.printLottos(lottos); + + List inputWinningNumbers = inputView.readWinningNumbers(); + WinningNumbers winningNumbers = new WinningNumbers(inputWinningNumbers); + + outputView.printLottoStatistics(lottos, winningNumbers, purchaseAmount); } } diff --git a/src/main/java/domain/LottoStatistics.java b/src/main/java/domain/LottoStatistics.java new file mode 100644 index 000000000..5feb46cb3 --- /dev/null +++ b/src/main/java/domain/LottoStatistics.java @@ -0,0 +1,64 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; + +public class LottoStatistics { + private static final long THREE_MATCH_PRIZE = 5000; + private static final long FOUR_MATCH_PRIZE = 50000; + private static final long FIVE_MATCH_PRIZE = 1500000; + private static final long SIX_MATCH_PRIZE = 2000000000; + private static final int NUMBER_BOUND = 6; + + private final List winningCounts = new ArrayList<>(7); + + public LottoStatistics(Lottos lottos, WinningNumbers winningNumbers) { + initializeWinningCounts(); + + calculateWinningCounts(lottos, winningNumbers); + } + + public int getWinningCount(int matchCount) { + return winningCounts.get(matchCount); + } + + public long getPrizeAmount(int matchCount) { + if (matchCount == 3) return THREE_MATCH_PRIZE; + if (matchCount == 4) return FOUR_MATCH_PRIZE; + if (matchCount == 5) return FIVE_MATCH_PRIZE; + if (matchCount == 6) return SIX_MATCH_PRIZE; + return 0; + } + + public double calculateProfitRate(PurchaseAmount purchaseAmount) { + return (double)calculateTotalPrize() / purchaseAmount.getAmount(); + } + + private long calculateTotalPrize() { + long total = 0; + + total += getWinningCount(3) * THREE_MATCH_PRIZE; + total += getWinningCount(4) * FOUR_MATCH_PRIZE; + total += getWinningCount(5) * FIVE_MATCH_PRIZE; + total += getWinningCount(6) * SIX_MATCH_PRIZE; + + return total; + } + + private void initializeWinningCounts() { + for (int i = 0; i <= NUMBER_BOUND; i++) { + winningCounts.add(0); + } + } + + private void calculateWinningCounts(Lottos lottos, WinningNumbers winningNumbers) { + for (Lotto lotto : lottos.getLottos()) { + int countMatchingNumbers = winningNumbers.countMatchingNumbers(lotto); + increaseWinningCount(countMatchingNumbers); + } + } + + private void increaseWinningCount(int countMatchingNumbers) { + winningCounts.set(countMatchingNumbers, winningCounts.get(countMatchingNumbers) + 1); + } +} diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java index f83f71c3e..2eac37f23 100644 --- a/src/main/java/domain/PurchaseAmount.java +++ b/src/main/java/domain/PurchaseAmount.java @@ -13,6 +13,10 @@ public PurchaseAmount(int amount) { this.amount = amount; } + public int getAmount() { + return amount; + } + public int calculateLottoCount() { return amount / LOTTO_PRICE; } diff --git a/src/main/java/domain/WinningNumbers.java b/src/main/java/domain/WinningNumbers.java new file mode 100644 index 000000000..0679ba7c4 --- /dev/null +++ b/src/main/java/domain/WinningNumbers.java @@ -0,0 +1,57 @@ +package domain; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class WinningNumbers { + private static final int WINNING_NUMBER_COUNT = 6; + + private final List winningNumbers = new ArrayList<>(); + + public WinningNumbers(List numbers) { + if (numbers.size() != WINNING_NUMBER_COUNT) { + throw new IllegalArgumentException("당첨 번호는 6개여야 합니다."); + } + + List lottoNumbers = convertToLottoNumbers(numbers); + + if (new HashSet<>(lottoNumbers).size() != lottoNumbers.size()) { + throw new IllegalArgumentException("당첨 번호는 중복될 수 없습니다."); + } + + winningNumbers.addAll(lottoNumbers); + } + + int countMatchingNumbers(Lotto lotto) { + int count = 0; + + for (LottoNumber lottoNumber : lotto.getNumbers()) { + count += countMatch(lottoNumber); + } + + return count; + } + + private int countMatch(LottoNumber lottoNumber) { + if (isWinningNumber(lottoNumber)) { + return 1; + } + + return 0; + } + + private boolean isWinningNumber(LottoNumber lottoNumber) { + return winningNumbers.contains(lottoNumber); + } + + private List convertToLottoNumbers(List numbers) { + List lottoNumbers = new ArrayList<>(); + + for (int number : numbers) { + lottoNumbers.add(new LottoNumber(number)); + } + + return lottoNumbers; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 799c28a2f..4965aff9d 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,5 +1,7 @@ package view; +import java.util.ArrayList; +import java.util.List; import java.util.Scanner; public class InputView { @@ -7,9 +9,28 @@ public class InputView { public int readPurchaseAmount() { System.out.println("구입금액을 입력해 주세요."); - int purchaseAmount = scanner.nextInt(); + String inputPurchaseAmount = scanner.nextLine(); + int purchaseAmount = Integer.parseInt(inputPurchaseAmount); + System.out.println(); return purchaseAmount; } + + public List readWinningNumbers() { + System.out.println("지난 주 당첨 번호를 입력해 주세요."); + String inputWinningNumbers = scanner.nextLine(); + System.out.println(); + + String[] splitWinningNumbers = inputWinningNumbers.split(","); + + List winningNumbers = new ArrayList<>(); + + for (String splitWinningNumber : splitWinningNumbers) { + splitWinningNumber = splitWinningNumber.trim(); + winningNumbers.add(Integer.parseInt(splitWinningNumber)); + } + + return winningNumbers; + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index a84a911e0..f3561e76c 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,14 +1,14 @@ package view; -import domain.Lotto; -import domain.LottoNumber; -import domain.Lottos; -import domain.PurchaseAmount; +import domain.*; import java.util.ArrayList; import java.util.List; public class OutputView { + private static final int MIN_WINNING_MATCH_COUNT = 3; + private static final int MAX_WINNING_MATCH_COUNT = 6; + public void printPurchasedCount(PurchaseAmount purchaseAmount) { System.out.println(purchaseAmount.calculateLottoCount() + "개를 구매했습니다."); } @@ -17,6 +17,16 @@ public void printLottos(Lottos lottos) { for (Lotto lotto : lottos.getLottos()) { printLotto(lotto); } + System.out.println(); + } + + public void printLottoStatistics(Lottos lottos, WinningNumbers winningNumbers, PurchaseAmount purchaseAmount) { + printStatisticsHeader(); + + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers); + printWinningCounts(lottoStatistics); + + printProfitRate(lottoStatistics, purchaseAmount); } private void printLotto(Lotto lotto) { @@ -28,4 +38,21 @@ private void printLotto(Lotto lotto) { System.out.println(numberValues); } + + private void printStatisticsHeader() { + System.out.println("당첨 통계"); + System.out.println("---------"); + } + + private void printWinningCounts(LottoStatistics lottoStatistics) { + for (int matchCount = MIN_WINNING_MATCH_COUNT; matchCount <= MAX_WINNING_MATCH_COUNT; matchCount++) { + System.out.println(matchCount + "개 일치 (" + + lottoStatistics.getPrizeAmount(matchCount) + ")- " + + lottoStatistics.getWinningCount(matchCount) + "개"); + } + } + + private void printProfitRate(LottoStatistics lottoStatistics, PurchaseAmount purchaseAmount) { + System.out.println("총 수익률은 " + lottoStatistics.calculateProfitRate(purchaseAmount) + "입니다."); + } } diff --git a/src/test/java/domain/WinningNumbersTest.java b/src/test/java/domain/WinningNumbersTest.java new file mode 100644 index 000000000..a9322bb13 --- /dev/null +++ b/src/test/java/domain/WinningNumbersTest.java @@ -0,0 +1,39 @@ +package domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class WinningNumbersTest { + @Test + @DisplayName("당첨 번호가 정확히 6개면 생성된다") + void createsWinningNumbersWithSixNumbers() { + List numbers = List.of(1, 2, 3, 4, 5, 6); + WinningNumbers winningNumbers = new WinningNumbers(numbers); + } + + @Test + @DisplayName("당첨 번호의 개수가 6개가 아니면 예외가 발생한다") + void throwsExceptionWhenWinningNumberCountIsInvalid() { + List numbers = List.of(1, 2, 3, 4, 5); + + assertThrows( + IllegalArgumentException.class, + () -> new WinningNumbers(numbers) + ); + } + + @Test + @DisplayName("당첨 번호에 중복된 번호가 있으면 예외가 발생한다") + void throwsExceptionWhenWinningNumbersContainDuplicates() { + List numbers = List.of(1, 2, 3, 4, 5, 5); + + assertThrows( + IllegalArgumentException.class, + () -> new WinningNumbers(numbers) + ); + } +} From 2edb4e40441346b1a71478ed69230e78eb17bfa6 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 16:06:00 +0900 Subject: [PATCH 09/28] =?UTF-8?q?feat:=20=EB=B3=B4=EB=84=88=EC=8A=A4=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EB=A5=BC=20=EB=B0=98=EC=98=81=ED=95=9C=202?= =?UTF-8?q?=EB=93=B1=20=EB=8B=B9=EC=B2=A8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 8 +++- src/main/java/domain/LottoStatistics.java | 45 +++++++++++++++++--- src/main/java/domain/WinningNumbers.java | 6 +++ src/main/java/view/InputView.java | 10 +++++ src/main/java/view/OutputView.java | 15 ++++++- src/test/java/domain/WinningNumbersTest.java | 14 ++++++ 6 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index cfa982a8c..3fb34d312 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,3 +1,4 @@ +import domain.LottoNumber; import domain.Lottos; import domain.PurchaseAmount; import domain.WinningNumbers; @@ -20,8 +21,13 @@ public static void main(String[] args) { outputView.printLottos(lottos); List inputWinningNumbers = inputView.readWinningNumbers(); + int inputBonusNumber = inputView.readBonusNumber(); + WinningNumbers winningNumbers = new WinningNumbers(inputWinningNumbers); + LottoNumber bonusNumber = new LottoNumber(inputBonusNumber); + + winningNumbers.validateBonusNumber(bonusNumber); - outputView.printLottoStatistics(lottos, winningNumbers, purchaseAmount); + outputView.printLottoStatistics(lottos, winningNumbers, purchaseAmount, bonusNumber); } } diff --git a/src/main/java/domain/LottoStatistics.java b/src/main/java/domain/LottoStatistics.java index 5feb46cb3..406a9eb0c 100644 --- a/src/main/java/domain/LottoStatistics.java +++ b/src/main/java/domain/LottoStatistics.java @@ -7,15 +7,19 @@ public class LottoStatistics { private static final long THREE_MATCH_PRIZE = 5000; private static final long FOUR_MATCH_PRIZE = 50000; private static final long FIVE_MATCH_PRIZE = 1500000; + private static final long BONUS_MATCH_PRIZE = 30000000; private static final long SIX_MATCH_PRIZE = 2000000000; - private static final int NUMBER_BOUND = 6; + private static final int MAX_MATCH_COUNT = 6; + private static final int BONUS_MATCH_COUNT = 5; private final List winningCounts = new ArrayList<>(7); - public LottoStatistics(Lottos lottos, WinningNumbers winningNumbers) { + private int bonusWinningCount = 0; + + public LottoStatistics(Lottos lottos, WinningNumbers winningNumbers, LottoNumber bonusNumber) { initializeWinningCounts(); - calculateWinningCounts(lottos, winningNumbers); + calculateWinningCounts(lottos, winningNumbers, bonusNumber); } public int getWinningCount(int matchCount) { @@ -30,6 +34,14 @@ public long getPrizeAmount(int matchCount) { return 0; } + public int getBonusWinningCount() { + return bonusWinningCount; + } + + public long getBonusPrizeAmount() { + return BONUS_MATCH_PRIZE; + } + public double calculateProfitRate(PurchaseAmount purchaseAmount) { return (double)calculateTotalPrize() / purchaseAmount.getAmount(); } @@ -40,25 +52,44 @@ private long calculateTotalPrize() { total += getWinningCount(3) * THREE_MATCH_PRIZE; total += getWinningCount(4) * FOUR_MATCH_PRIZE; total += getWinningCount(5) * FIVE_MATCH_PRIZE; + total += getBonusWinningCount() * BONUS_MATCH_PRIZE; total += getWinningCount(6) * SIX_MATCH_PRIZE; return total; } private void initializeWinningCounts() { - for (int i = 0; i <= NUMBER_BOUND; i++) { + for (int i = 0; i <= MAX_MATCH_COUNT; i++) { winningCounts.add(0); } } - private void calculateWinningCounts(Lottos lottos, WinningNumbers winningNumbers) { + private void calculateWinningCounts(Lottos lottos, WinningNumbers winningNumbers, LottoNumber bonusNumber) { for (Lotto lotto : lottos.getLottos()) { - int countMatchingNumbers = winningNumbers.countMatchingNumbers(lotto); - increaseWinningCount(countMatchingNumbers); + processLottoResult(lotto, winningNumbers, bonusNumber); } } private void increaseWinningCount(int countMatchingNumbers) { winningCounts.set(countMatchingNumbers, winningCounts.get(countMatchingNumbers) + 1); } + + private void increaseBonusWinningCount() { + bonusWinningCount++; + } + + private boolean isBonusNumberMatched(Lotto lotto, LottoNumber bonusNumber) { + return lotto.getNumbers().contains(bonusNumber); + } + + private void processLottoResult(Lotto lotto, WinningNumbers winningNumbers, LottoNumber bonusNumber) { + int countMatchingNumbers = winningNumbers.countMatchingNumbers(lotto); + + if (countMatchingNumbers == BONUS_MATCH_COUNT && isBonusNumberMatched(lotto, bonusNumber)) { + increaseBonusWinningCount(); + return; + } + + increaseWinningCount(countMatchingNumbers); + } } diff --git a/src/main/java/domain/WinningNumbers.java b/src/main/java/domain/WinningNumbers.java index 0679ba7c4..defb02ad2 100644 --- a/src/main/java/domain/WinningNumbers.java +++ b/src/main/java/domain/WinningNumbers.java @@ -23,6 +23,12 @@ public WinningNumbers(List numbers) { winningNumbers.addAll(lottoNumbers); } + public void validateBonusNumber(LottoNumber bonusNumber) { + if (winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); + } + } + int countMatchingNumbers(Lotto lotto) { int count = 0; diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 4965aff9d..7e6b4bfd3 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -33,4 +33,14 @@ public List readWinningNumbers() { return winningNumbers; } + + public int readBonusNumber() { + System.out.println("보너스 볼을 입력해 주세요."); + String inputBonusNumber = scanner.nextLine(); + int bonusNumber = Integer.parseInt(inputBonusNumber); + + System.out.println(); + + return bonusNumber; + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index f3561e76c..b1b83dac6 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -8,6 +8,7 @@ public class OutputView { private static final int MIN_WINNING_MATCH_COUNT = 3; private static final int MAX_WINNING_MATCH_COUNT = 6; + private static final int BONUS_MATCH_COUNT = 5; public void printPurchasedCount(PurchaseAmount purchaseAmount) { System.out.println(purchaseAmount.calculateLottoCount() + "개를 구매했습니다."); @@ -20,10 +21,11 @@ public void printLottos(Lottos lottos) { System.out.println(); } - public void printLottoStatistics(Lottos lottos, WinningNumbers winningNumbers, PurchaseAmount purchaseAmount) { + public void printLottoStatistics(Lottos lottos, WinningNumbers winningNumbers, + PurchaseAmount purchaseAmount, LottoNumber bonusNumber) { printStatisticsHeader(); - LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers); + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); printWinningCounts(lottoStatistics); printProfitRate(lottoStatistics, purchaseAmount); @@ -49,10 +51,19 @@ private void printWinningCounts(LottoStatistics lottoStatistics) { System.out.println(matchCount + "개 일치 (" + lottoStatistics.getPrizeAmount(matchCount) + ")- " + lottoStatistics.getWinningCount(matchCount) + "개"); + printBonusWinningCountIfNeeded(lottoStatistics, matchCount); } } private void printProfitRate(LottoStatistics lottoStatistics, PurchaseAmount purchaseAmount) { System.out.println("총 수익률은 " + lottoStatistics.calculateProfitRate(purchaseAmount) + "입니다."); } + + private void printBonusWinningCountIfNeeded(LottoStatistics lottoStatistics, int matchCount) { + if (matchCount == BONUS_MATCH_COUNT) { + System.out.println(matchCount + "개 일치, 보너스 볼 일치(" + + lottoStatistics.getBonusPrizeAmount() + ") - " + + lottoStatistics.getBonusWinningCount() + "개"); + } + } } diff --git a/src/test/java/domain/WinningNumbersTest.java b/src/test/java/domain/WinningNumbersTest.java index a9322bb13..67d22f5e4 100644 --- a/src/test/java/domain/WinningNumbersTest.java +++ b/src/test/java/domain/WinningNumbersTest.java @@ -36,4 +36,18 @@ void throwsExceptionWhenWinningNumbersContainDuplicates() { () -> new WinningNumbers(numbers) ); } + + @Test + @DisplayName("보너스 번호가 당첨 번호와 중복되면 예외가 발생한다") + void throwsExceptionWhenBonusNumberIsDuplicatedWithWinningNumbers() { + List numbers = List.of(1, 2, 3, 4, 5, 6); + LottoNumber bonusNumber = new LottoNumber(3); + + WinningNumbers winningNumbers = new WinningNumbers(numbers); + + assertThrows( + IllegalArgumentException.class, + () -> winningNumbers.validateBonusNumber(bonusNumber) + ); + } } From 6cd46afe9a094712061dbbf338f779a7a410949f Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 17:24:04 +0900 Subject: [PATCH 10/28] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=8B=B9=EC=B2=A8=20=EB=93=B1=EC=88=98=EC=99=80=20=EC=83=81?= =?UTF-8?q?=EA=B8=88=20=EA=B4=80=EB=A6=AC=EB=A5=BC=20Enum=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/LottoRank.java | 34 ++++++++++++ src/main/java/domain/LottoStatistics.java | 66 +++++++---------------- src/main/java/view/OutputView.java | 25 ++++----- 3 files changed, 63 insertions(+), 62 deletions(-) create mode 100644 src/main/java/domain/LottoRank.java diff --git a/src/main/java/domain/LottoRank.java b/src/main/java/domain/LottoRank.java new file mode 100644 index 000000000..336f2fb88 --- /dev/null +++ b/src/main/java/domain/LottoRank.java @@ -0,0 +1,34 @@ +package domain; + +public enum LottoRank { + FIFTH(3, 5000), + FOURTH(4,50000), + THIRD(5, 1500000), + SECOND(5, 30000000), + FIRST(6, 2000000000); + + private final int matchCount; + private final long prize; + + LottoRank(int matchCount, long prize) { + this.matchCount = matchCount; + this.prize = prize; + } + + public static LottoRank from(int matchCount, boolean bonusMatched) { + if (matchCount == 6) return FIRST; + if (matchCount == 5 && bonusMatched) return SECOND; + if (matchCount == 5) return THIRD; + if (matchCount == 4) return FOURTH; + if (matchCount == 3) return FIFTH; + return null; + } + + public int getMatchCount() { + return matchCount; + } + + public long getPrize() { + return prize; + } +} diff --git a/src/main/java/domain/LottoStatistics.java b/src/main/java/domain/LottoStatistics.java index 406a9eb0c..168cba135 100644 --- a/src/main/java/domain/LottoStatistics.java +++ b/src/main/java/domain/LottoStatistics.java @@ -1,20 +1,10 @@ package domain; -import java.util.ArrayList; -import java.util.List; +import java.util.EnumMap; +import java.util.Map; public class LottoStatistics { - private static final long THREE_MATCH_PRIZE = 5000; - private static final long FOUR_MATCH_PRIZE = 50000; - private static final long FIVE_MATCH_PRIZE = 1500000; - private static final long BONUS_MATCH_PRIZE = 30000000; - private static final long SIX_MATCH_PRIZE = 2000000000; - private static final int MAX_MATCH_COUNT = 6; - private static final int BONUS_MATCH_COUNT = 5; - - private final List winningCounts = new ArrayList<>(7); - - private int bonusWinningCount = 0; + private final Map winningCounts = new EnumMap<>(LottoRank.class); public LottoStatistics(Lottos lottos, WinningNumbers winningNumbers, LottoNumber bonusNumber) { initializeWinningCounts(); @@ -22,24 +12,8 @@ public LottoStatistics(Lottos lottos, WinningNumbers winningNumbers, LottoNumber calculateWinningCounts(lottos, winningNumbers, bonusNumber); } - public int getWinningCount(int matchCount) { - return winningCounts.get(matchCount); - } - - public long getPrizeAmount(int matchCount) { - if (matchCount == 3) return THREE_MATCH_PRIZE; - if (matchCount == 4) return FOUR_MATCH_PRIZE; - if (matchCount == 5) return FIVE_MATCH_PRIZE; - if (matchCount == 6) return SIX_MATCH_PRIZE; - return 0; - } - - public int getBonusWinningCount() { - return bonusWinningCount; - } - - public long getBonusPrizeAmount() { - return BONUS_MATCH_PRIZE; + public int getWinningCount(LottoRank rank) { + return winningCounts.get(rank); } public double calculateProfitRate(PurchaseAmount purchaseAmount) { @@ -49,18 +23,16 @@ public double calculateProfitRate(PurchaseAmount purchaseAmount) { private long calculateTotalPrize() { long total = 0; - total += getWinningCount(3) * THREE_MATCH_PRIZE; - total += getWinningCount(4) * FOUR_MATCH_PRIZE; - total += getWinningCount(5) * FIVE_MATCH_PRIZE; - total += getBonusWinningCount() * BONUS_MATCH_PRIZE; - total += getWinningCount(6) * SIX_MATCH_PRIZE; + for (LottoRank rank : LottoRank.values()) { + total += getWinningCount(rank) * rank.getPrize(); + } return total; } private void initializeWinningCounts() { - for (int i = 0; i <= MAX_MATCH_COUNT; i++) { - winningCounts.add(0); + for (LottoRank rank : LottoRank.values()) { + winningCounts.put(rank, 0); } } @@ -70,12 +42,8 @@ private void calculateWinningCounts(Lottos lottos, WinningNumbers winningNumbers } } - private void increaseWinningCount(int countMatchingNumbers) { - winningCounts.set(countMatchingNumbers, winningCounts.get(countMatchingNumbers) + 1); - } - - private void increaseBonusWinningCount() { - bonusWinningCount++; + private void increaseWinningCount(LottoRank rank) { + winningCounts.put(rank, winningCounts.get(rank) + 1); } private boolean isBonusNumberMatched(Lotto lotto, LottoNumber bonusNumber) { @@ -83,13 +51,15 @@ private boolean isBonusNumberMatched(Lotto lotto, LottoNumber bonusNumber) { } private void processLottoResult(Lotto lotto, WinningNumbers winningNumbers, LottoNumber bonusNumber) { - int countMatchingNumbers = winningNumbers.countMatchingNumbers(lotto); + int matchCount = winningNumbers.countMatchingNumbers(lotto); + boolean bonusMatched = isBonusNumberMatched(lotto, bonusNumber); + + LottoRank rank = LottoRank.from(matchCount, bonusMatched); - if (countMatchingNumbers == BONUS_MATCH_COUNT && isBonusNumberMatched(lotto, bonusNumber)) { - increaseBonusWinningCount(); + if (rank == null) { return; } - increaseWinningCount(countMatchingNumbers); + increaseWinningCount(rank); } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index b1b83dac6..be5f135e2 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -6,10 +6,6 @@ import java.util.List; public class OutputView { - private static final int MIN_WINNING_MATCH_COUNT = 3; - private static final int MAX_WINNING_MATCH_COUNT = 6; - private static final int BONUS_MATCH_COUNT = 5; - public void printPurchasedCount(PurchaseAmount purchaseAmount) { System.out.println(purchaseAmount.calculateLottoCount() + "개를 구매했습니다."); } @@ -47,11 +43,8 @@ private void printStatisticsHeader() { } private void printWinningCounts(LottoStatistics lottoStatistics) { - for (int matchCount = MIN_WINNING_MATCH_COUNT; matchCount <= MAX_WINNING_MATCH_COUNT; matchCount++) { - System.out.println(matchCount + "개 일치 (" - + lottoStatistics.getPrizeAmount(matchCount) + ")- " - + lottoStatistics.getWinningCount(matchCount) + "개"); - printBonusWinningCountIfNeeded(lottoStatistics, matchCount); + for (LottoRank rank : LottoRank.values()) { + printWinningResult(lottoStatistics, rank); } } @@ -59,11 +52,15 @@ private void printProfitRate(LottoStatistics lottoStatistics, PurchaseAmount pur System.out.println("총 수익률은 " + lottoStatistics.calculateProfitRate(purchaseAmount) + "입니다."); } - private void printBonusWinningCountIfNeeded(LottoStatistics lottoStatistics, int matchCount) { - if (matchCount == BONUS_MATCH_COUNT) { - System.out.println(matchCount + "개 일치, 보너스 볼 일치(" - + lottoStatistics.getBonusPrizeAmount() + ") - " - + lottoStatistics.getBonusWinningCount() + "개"); + private void printWinningResult(LottoStatistics lottoStatistics, LottoRank rank) { + if (rank == LottoRank.SECOND) { + System.out.println(rank.getMatchCount() + "개 일치, 보너스 볼 일치(" + + rank.getPrize() + "원) - " + + lottoStatistics.getWinningCount(rank) + "개"); + return; } + System.out.println(rank.getMatchCount() + "개 일치 (" + + rank.getPrize() + "원)- " + + lottoStatistics.getWinningCount(rank) + "개"); } } From 3356338b8111fbc38bad2e98e7ec6ac55de76299 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 20:58:24 +0900 Subject: [PATCH 11/28] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EC=88=98?= =?UTF-8?q?=EB=8F=99=20=EA=B5=AC=EB=A7=A4=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 24 +++++----- src/main/java/domain/Lotto.java | 26 +++++++++-- src/main/java/domain/Lottos.java | 8 ++-- src/main/java/domain/PurchaseAmount.java | 19 +++++++- src/main/java/view/InputView.java | 46 ++++++++++++++++---- src/main/java/view/OutputView.java | 4 +- src/test/java/domain/LottoTest.java | 23 ++++++++++ src/test/java/domain/LottosTest.java | 31 +++++++++++-- src/test/java/domain/PurchaseAmountTest.java | 33 ++++++++++++++ 9 files changed, 179 insertions(+), 35 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 3fb34d312..ffacdbc20 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,7 +1,4 @@ -import domain.LottoNumber; -import domain.Lottos; -import domain.PurchaseAmount; -import domain.WinningNumbers; +import domain.*; import view.InputView; import view.OutputView; @@ -12,19 +9,20 @@ public static void main(String[] args) { InputView inputView = new InputView(); OutputView outputView = new OutputView(); - int inputAmount = inputView.readPurchaseAmount(); - PurchaseAmount purchaseAmount = new PurchaseAmount(inputAmount); + PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readPurchaseAmount()); - Lottos lottos = new Lottos(purchaseAmount); + int manualLottoCount = inputView.readManualLottoCount(); + int autoLottoCount = purchaseAmount.calculateAutoLottoCount(manualLottoCount); - outputView.printPurchasedCount(purchaseAmount); - outputView.printLottos(lottos); + List> manualLottoNumbers = inputView.readManualLottoNumbers(manualLottoCount); + + Lottos lottos = new Lottos(manualLottoNumbers, autoLottoCount); - List inputWinningNumbers = inputView.readWinningNumbers(); - int inputBonusNumber = inputView.readBonusNumber(); + outputView.printPurchasedCount(manualLottoCount, autoLottoCount); + outputView.printLottos(lottos); - WinningNumbers winningNumbers = new WinningNumbers(inputWinningNumbers); - LottoNumber bonusNumber = new LottoNumber(inputBonusNumber); + WinningNumbers winningNumbers = new WinningNumbers(inputView.readWinningNumbers()); + LottoNumber bonusNumber = new LottoNumber(inputView.readBonusNumber()); winningNumbers.validateBonusNumber(bonusNumber); diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 363bb676e..ee325176e 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -1,9 +1,6 @@ package domain; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Random; +import java.util.*; public class Lotto { private static final int NUMBER_COUNT = 6; @@ -16,6 +13,20 @@ public Lotto() { makeNumbers(); } + public Lotto(List numberValues) { + if (numberValues.size() != NUMBER_COUNT) { + throw new IllegalArgumentException("로또 번호는 " + NUMBER_COUNT + "개여야 합니다."); + } + + if (new HashSet<>(numberValues).size() != numberValues.size()) { + throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); + } + + convertToLottoNumbers(numberValues); + + Collections.sort(numbers); + } + public List getNumbers() { return List.copyOf(numbers); } @@ -41,4 +52,11 @@ private LottoNumber generateRandomNumber() { private boolean containsNumber(LottoNumber lottoNumber) { return numbers.contains(lottoNumber); } + + private void convertToLottoNumbers(List numberValues) { + for (int number : numberValues) { + LottoNumber lottoNumber = new LottoNumber(number); + numbers.add(lottoNumber); + } + } } diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 59a9a5b96..3e1d84552 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -6,10 +6,12 @@ public class Lottos { private final List lottos = new ArrayList<>(); - public Lottos(PurchaseAmount purchaseAmount) { - int lottoCount = purchaseAmount.calculateLottoCount(); + public Lottos(List> manualLottoNumbers, int autoLottoCount) { + for (List manualLottoNumber : manualLottoNumbers) { + lottos.add(new Lotto(manualLottoNumber)); + } - for (int i = 0; i < lottoCount; i++) { + for (int i = 0; i < autoLottoCount; i++) { lottos.add(new Lotto()); } } diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java index 2eac37f23..e1d756239 100644 --- a/src/main/java/domain/PurchaseAmount.java +++ b/src/main/java/domain/PurchaseAmount.java @@ -2,12 +2,13 @@ public class PurchaseAmount { private static final int LOTTO_PRICE = 1000; + private static final int MIN_MANUAL_LOTTO_COUNT = 0; private final int amount; public PurchaseAmount(int amount) { if (amount < LOTTO_PRICE) { - throw new IllegalArgumentException("최소 구입 금액은 1000원 입니다."); + throw new IllegalArgumentException("최소 구입 금액은 " + LOTTO_PRICE + "원 입니다."); } this.amount = amount; @@ -20,4 +21,20 @@ public int getAmount() { public int calculateLottoCount() { return amount / LOTTO_PRICE; } + + public int calculateAutoLottoCount(int manualLottoCount) { + validateManualLottoCount(manualLottoCount); + + return calculateLottoCount() - manualLottoCount; + } + + private void validateManualLottoCount(int manualLottoCount) { + if (manualLottoCount > calculateLottoCount()) { + throw new IllegalArgumentException("수동 구매 개수는 전체 구매 가능 개수를 초과할 수 없습니다."); + } + + if (manualLottoCount < MIN_MANUAL_LOTTO_COUNT) { + throw new IllegalArgumentException("수동 구매 개수는 " + MIN_MANUAL_LOTTO_COUNT + "이상이어야 합니다."); + } + } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 7e6b4bfd3..0824dd034 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -17,21 +17,37 @@ public int readPurchaseAmount() { return purchaseAmount; } - public List readWinningNumbers() { - System.out.println("지난 주 당첨 번호를 입력해 주세요."); - String inputWinningNumbers = scanner.nextLine(); + public int readManualLottoCount() { + System.out.println("수동으로 구매할 로또 수를 입력해 주세요."); + String inputManualLottoCount = scanner.nextLine(); + int manualLottoCount = Integer.parseInt(inputManualLottoCount); + System.out.println(); - String[] splitWinningNumbers = inputWinningNumbers.split(","); + return manualLottoCount; + } + + public List> readManualLottoNumbers(int manualLottoCount) { + List> manualLottoNumbers = new ArrayList<>(); - List winningNumbers = new ArrayList<>(); + System.out.println("수동으로 구매할 번호를 입력해 주세요."); - for (String splitWinningNumber : splitWinningNumbers) { - splitWinningNumber = splitWinningNumber.trim(); - winningNumbers.add(Integer.parseInt(splitWinningNumber)); + for (int i = 0; i < manualLottoCount; i++) { + String inputNumbers = scanner.nextLine(); + manualLottoNumbers.add(parseNumbers(inputNumbers)); } - return winningNumbers; + System.out.println(); + + return manualLottoNumbers; + } + + public List readWinningNumbers() { + System.out.println("지난 주 당첨 번호를 입력해 주세요."); + String winningNumbers = scanner.nextLine(); + System.out.println(); + + return parseNumbers(winningNumbers); } public int readBonusNumber() { @@ -43,4 +59,16 @@ public int readBonusNumber() { return bonusNumber; } + + private List parseNumbers(String input) { + List numbers = new ArrayList<>(); + + String[] numberStrings = input.split(","); + + for (String numberString : numberStrings) { + numbers.add(Integer.parseInt(numberString.trim())); + } + + return numbers; + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index be5f135e2..fec3002b5 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -6,8 +6,8 @@ import java.util.List; public class OutputView { - public void printPurchasedCount(PurchaseAmount purchaseAmount) { - System.out.println(purchaseAmount.calculateLottoCount() + "개를 구매했습니다."); + public void printPurchasedCount(int manualLottoCount, int autoLottoCount) { + System.out.println("수동으로 " + manualLottoCount + "장, 자동으로 " + autoLottoCount + "개를 구매했습니다."); } public void printLottos(Lottos lottos) { diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java index 353961131..883b2885f 100644 --- a/src/test/java/domain/LottoTest.java +++ b/src/test/java/domain/LottoTest.java @@ -1,9 +1,15 @@ package domain; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; public class LottoTest { @Test @@ -31,4 +37,21 @@ void lottoShouldContainsSizNumbers() { assertThat(lotto.getNumbers()).hasSize(6); } + + @ParameterizedTest + @MethodSource("invalidLottoNumbers") + @DisplayName("로또 번호가 6개가 아니면 예외가 발생한다") + void throwsExceptionWhenLottoNumberCountIsNotSix(List numbers) { + assertThrows( + IllegalArgumentException.class, + () -> new Lotto(numbers) + ); + } + + static Stream> invalidLottoNumbers() { + return Stream.of( + List.of(1, 2, 3, 4, 5), + List.of(1, 2, 3, 4, 5, 6, 7) + ); + } } diff --git a/src/test/java/domain/LottosTest.java b/src/test/java/domain/LottosTest.java index 9db5d0258..ac7d10c1b 100644 --- a/src/test/java/domain/LottosTest.java +++ b/src/test/java/domain/LottosTest.java @@ -5,12 +5,37 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.util.List; + public class LottosTest { @Test - @DisplayName("구입 금액으로 계산된 개수만큼 로또를 생성한다") - void createsLottosBasedOnPurchaseAmount() { + @DisplayName("수동 로또와 자동 로또를 합한 개수만큼 로또를 생성한다") + void createsManualAndAutoLottos() { + PurchaseAmount purchaseAmount = new PurchaseAmount(12000); + + int manualLottoCount = 3; + int autoLottoCount = purchaseAmount.calculateAutoLottoCount(manualLottoCount); + List> manualLottoNumbers = List.of( + List.of(1, 2, 3, 4, 5, 6), + List.of(7, 8, 9, 10, 11, 12), + List.of(13, 14, 15, 16, 17, 18) + ); + + Lottos lottos = new Lottos(manualLottoNumbers, autoLottoCount); + + assertEquals(12, lottos.getLottos().size()); + } + + @Test + @DisplayName("수동 로또가 없으면 자동 로또만 생성한다") + void createsOnlyAutoLottosWhenThereAreNoManualLottos() { PurchaseAmount purchaseAmount = new PurchaseAmount(14000); - Lottos lottos = new Lottos(purchaseAmount); + + int manualLottoCount = 0; + int autoLottoCount = purchaseAmount.calculateAutoLottoCount(manualLottoCount); + List> manualLottoNumbers = List.of(); + + Lottos lottos = new Lottos(manualLottoNumbers, autoLottoCount); assertEquals(14, lottos.getLottos().size()); } diff --git a/src/test/java/domain/PurchaseAmountTest.java b/src/test/java/domain/PurchaseAmountTest.java index a6f0622cb..04ca10f49 100644 --- a/src/test/java/domain/PurchaseAmountTest.java +++ b/src/test/java/domain/PurchaseAmountTest.java @@ -34,4 +34,37 @@ void throwsExceptionWhenPurchaseAmountIsLessThanLottoPrice(int amount) { () -> new PurchaseAmount(amount) ); } + + @Test + @DisplayName("수동 구매 개수가 음수이면 예외가 발생한다") + void throwsExceptionWhenManualLottoCountIsNegative() { + PurchaseAmount purchaseAmount = new PurchaseAmount(14000); + int manualLottoCount = -2; + + assertThrows( + IllegalArgumentException.class, + () -> purchaseAmount.calculateAutoLottoCount(manualLottoCount) + ); + } + + @Test + @DisplayName("수동 구매 개수가 전체 구매 가능 개수를 초과하면 예외가 발생한다") + void throwsExceptionWhenManualLottoCountExceedsTotalLottoCount() { + PurchaseAmount purchaseAmount = new PurchaseAmount(14000); + int manualLottoCount = 15; + + assertThrows( + IllegalArgumentException.class, + () -> purchaseAmount.calculateAutoLottoCount(manualLottoCount) + ); + } + + @Test + @DisplayName("수동 구매 개수를 제외한 자동 구매 개수를 계산한다") + void calculatesAutoLottoCountWithValidManualLottoCount() { + PurchaseAmount purchaseAmount = new PurchaseAmount(14000); + int manualLottoCount = 3; + + assertEquals(11, purchaseAmount.calculateAutoLottoCount(manualLottoCount)); + } } From bc267ed3e09efd5830b02c9e5165ff58906b0df7 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 21:26:55 +0900 Subject: [PATCH 12/28] =?UTF-8?q?refactor:=20=EC=99=80=EC=9D=BC=EB=93=9C?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20import=EB=A5=BC=20=EB=AA=85=EC=8B=9C?= =?UTF-8?q?=EC=A0=81=20import=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 6 +++++- src/main/java/domain/Lotto.java | 6 +++++- src/main/java/view/OutputView.java | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index ffacdbc20..5c11d8002 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,4 +1,8 @@ -import domain.*; +import domain.LottoNumber; +import domain.Lottos; +import domain.PurchaseAmount; +import domain.WinningNumbers; + import view.InputView; import view.OutputView; diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index ee325176e..723a90671 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -1,6 +1,10 @@ package domain; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Random; public class Lotto { private static final int NUMBER_COUNT = 6; diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index fec3002b5..157306205 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,6 +1,12 @@ package view; -import domain.*; +import domain.Lotto; +import domain.LottoNumber; +import domain.LottoRank; +import domain.LottoStatistics; +import domain.Lottos; +import domain.PurchaseAmount; +import domain.WinningNumbers; import java.util.ArrayList; import java.util.List; From 3fda90c7e3566a8c54a3bbf124b563a116089ce4 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 21:38:33 +0900 Subject: [PATCH 13/28] =?UTF-8?q?refactor:=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=B3=84=EB=8F=84=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 21 ++++++++++++++------- src/main/java/domain/LottoNumber.java | 10 +++++++--- src/main/java/domain/PurchaseAmount.java | 10 +++++++--- src/main/java/domain/WinningNumbers.java | 20 ++++++++++++++------ 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 723a90671..348811e75 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -18,13 +18,8 @@ public Lotto() { } public Lotto(List numberValues) { - if (numberValues.size() != NUMBER_COUNT) { - throw new IllegalArgumentException("로또 번호는 " + NUMBER_COUNT + "개여야 합니다."); - } - - if (new HashSet<>(numberValues).size() != numberValues.size()) { - throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); - } + validateNumberCount(numberValues); + validateDuplicateNumbers(numberValues); convertToLottoNumbers(numberValues); @@ -63,4 +58,16 @@ private void convertToLottoNumbers(List numberValues) { numbers.add(lottoNumber); } } + + private void validateNumberCount(List numberValues) { + if (numberValues.size() != NUMBER_COUNT) { + throw new IllegalArgumentException("로또 번호는 " + NUMBER_COUNT + "개여야 합니다."); + } + } + + private void validateDuplicateNumbers(List numberValues) { + if (new HashSet<>(numberValues).size() != numberValues.size()) { + throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); + } + } } diff --git a/src/main/java/domain/LottoNumber.java b/src/main/java/domain/LottoNumber.java index a2013d87d..e08a596d1 100644 --- a/src/main/java/domain/LottoNumber.java +++ b/src/main/java/domain/LottoNumber.java @@ -9,9 +9,7 @@ public class LottoNumber implements Comparable{ private final int number; public LottoNumber(int number) { - if (number < MIN_LOTTO_NUMBER || number > MAX_LOTTO_NUMBER) { - throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다."); - } + validateNumberRange(number); this.number = number; } @@ -44,4 +42,10 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hash(number); } + + private void validateNumberRange(int number) { + if (number < MIN_LOTTO_NUMBER || number > MAX_LOTTO_NUMBER) { + throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다."); + } + } } diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java index e1d756239..e6a18598d 100644 --- a/src/main/java/domain/PurchaseAmount.java +++ b/src/main/java/domain/PurchaseAmount.java @@ -7,9 +7,7 @@ public class PurchaseAmount { private final int amount; public PurchaseAmount(int amount) { - if (amount < LOTTO_PRICE) { - throw new IllegalArgumentException("최소 구입 금액은 " + LOTTO_PRICE + "원 입니다."); - } + validatePurchaseAmount(amount); this.amount = amount; } @@ -37,4 +35,10 @@ private void validateManualLottoCount(int manualLottoCount) { throw new IllegalArgumentException("수동 구매 개수는 " + MIN_MANUAL_LOTTO_COUNT + "이상이어야 합니다."); } } + + private void validatePurchaseAmount(int amount) { + if (amount < LOTTO_PRICE) { + throw new IllegalArgumentException("최소 구입 금액은 " + LOTTO_PRICE + "원 입니다."); + } + } } diff --git a/src/main/java/domain/WinningNumbers.java b/src/main/java/domain/WinningNumbers.java index defb02ad2..ce524c249 100644 --- a/src/main/java/domain/WinningNumbers.java +++ b/src/main/java/domain/WinningNumbers.java @@ -10,15 +10,11 @@ public class WinningNumbers { private final List winningNumbers = new ArrayList<>(); public WinningNumbers(List numbers) { - if (numbers.size() != WINNING_NUMBER_COUNT) { - throw new IllegalArgumentException("당첨 번호는 6개여야 합니다."); - } + validateWinningNumberCount(numbers); List lottoNumbers = convertToLottoNumbers(numbers); - if (new HashSet<>(lottoNumbers).size() != lottoNumbers.size()) { - throw new IllegalArgumentException("당첨 번호는 중복될 수 없습니다."); - } + validateDuplicateWinningNumbers(lottoNumbers); winningNumbers.addAll(lottoNumbers); } @@ -60,4 +56,16 @@ private List convertToLottoNumbers(List numbers) { return lottoNumbers; } + + private void validateWinningNumberCount(List numbers) { + if (numbers.size() != WINNING_NUMBER_COUNT) { + throw new IllegalArgumentException("당첨 번호는 6개여야 합니다."); + } + } + + private void validateDuplicateWinningNumbers(List lottoNumbers) { + if (new HashSet<>(lottoNumbers).size() != lottoNumbers.size()) { + throw new IllegalArgumentException("당첨 번호는 중복될 수 없습니다."); + } + } } From 779b1e8dcb265836ff00d60080b527be9db1bb9e Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 21:48:04 +0900 Subject: [PATCH 14/28] =?UTF-8?q?test:=20=EC=A0=84=EC=B2=B4=20=EC=88=98?= =?UTF-8?q?=EB=8F=99=20=EA=B5=AC=EB=A7=A4=20=EC=8B=9C=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=20=EA=B5=AC=EB=A7=A4=20=EA=B0=9C=EC=88=98=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/PurchaseAmountTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/domain/PurchaseAmountTest.java b/src/test/java/domain/PurchaseAmountTest.java index 04ca10f49..7730161d8 100644 --- a/src/test/java/domain/PurchaseAmountTest.java +++ b/src/test/java/domain/PurchaseAmountTest.java @@ -67,4 +67,13 @@ void calculatesAutoLottoCountWithValidManualLottoCount() { assertEquals(11, purchaseAmount.calculateAutoLottoCount(manualLottoCount)); } + + @Test + @DisplayName("구매 가능한 로또를 모두 수동으로 구매하면 자동 구매 개수는 0이다") + void calculateZeroAutoLottoCountWhenAllLottosAreManual() { + PurchaseAmount purchaseAmount = new PurchaseAmount(14000); + int manualLottoCount = 14; + + assertEquals(0, purchaseAmount.calculateAutoLottoCount(manualLottoCount)); + } } From e39822342b1222f38a23c1e199e3877b38cc367c Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 22:04:05 +0900 Subject: [PATCH 15/28] =?UTF-8?q?test:=20LottoRank=EC=99=80=20LottoStatist?= =?UTF-8?q?ics=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoRankTest.java | 57 +++++++++++++++++++ src/test/java/domain/LottoStatisticsTest.java | 35 ++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/test/java/domain/LottoRankTest.java create mode 100644 src/test/java/domain/LottoStatisticsTest.java diff --git a/src/test/java/domain/LottoRankTest.java b/src/test/java/domain/LottoRankTest.java new file mode 100644 index 000000000..52c6cccea --- /dev/null +++ b/src/test/java/domain/LottoRankTest.java @@ -0,0 +1,57 @@ +package domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class LottoRankTest { + @Test + @DisplayName("6개 번호가 일치하면 1등이다") + void returnsFirstWhenSixNumbersMatch() { + LottoRank rank = LottoRank.from(6, false); + + assertEquals(LottoRank.FIRST, rank); + } + + @Test + @DisplayName("5개 번호와 보너스 번호가 일치하면 2등이다") + void returnsSecondWhenFiveNumbersAndBonusMatch() { + LottoRank rank = LottoRank.from(5, true); + + assertEquals(LottoRank.SECOND, rank); + } + + @Test + @DisplayName("5개 번호가 일치하고 보너스 번호가 일치하지 않으면 3등이다") + void returnsThirdWhenFiveNumbersMatchWithoutBonus() { + LottoRank rank = LottoRank.from(5, false); + + assertEquals(LottoRank.THIRD, rank); + } + + @Test + @DisplayName("4개 번호가 일치하면 4등이다") + void returnsFourthWhenFourNumbersMatch() { + LottoRank rank = LottoRank.from(4, false); + + assertEquals(LottoRank.FOURTH, rank); + } + + @Test + @DisplayName("3개 번호가 일치하면 5등이다") + void returnsFifthWhenThreeNumbersMatch() { + LottoRank rank = LottoRank.from(3, false); + + assertEquals(LottoRank.FIFTH, rank); + } + + @Test + @DisplayName("일치하는 번호가 3개 미만이면 당첨되지 않는다") + void returnsNullWhenMatchCountIsLessThanThree() { + LottoRank rank = LottoRank.from(2, false); + + assertNull(rank); + } +} diff --git a/src/test/java/domain/LottoStatisticsTest.java b/src/test/java/domain/LottoStatisticsTest.java new file mode 100644 index 000000000..0be2e6000 --- /dev/null +++ b/src/test/java/domain/LottoStatisticsTest.java @@ -0,0 +1,35 @@ +package domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class LottoStatisticsTest { + @Test + @DisplayName("로또 당첨 결과를 등수별로 집계한다") + void calculatesWinningCountForEachRank() { + List winningNumberValues = List.of(1, 2, 3, 4, 5, 6); + WinningNumbers winningNumbers = new WinningNumbers(winningNumberValues); + LottoNumber bonusNumber = new LottoNumber(7); + + List> lottoNumbers = List.of( + List.of(1, 2, 3, 4, 5, 6), + List.of(1, 2, 3, 4, 5, 7), + List.of(1, 2, 3, 4, 5, 8), + List.of(1, 2, 3, 4, 8, 9), + List.of(1, 2, 3, 8, 9, 10) + ); + + Lottos lottos = new Lottos(lottoNumbers, 0); + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); + + assertEquals(1, lottoStatistics.getWinningCount(LottoRank.FIRST)); + assertEquals(1, lottoStatistics.getWinningCount(LottoRank.SECOND)); + assertEquals(1, lottoStatistics.getWinningCount(LottoRank.THIRD)); + assertEquals(1, lottoStatistics.getWinningCount(LottoRank.FOURTH)); + assertEquals(1, lottoStatistics.getWinningCount(LottoRank.FIFTH)); + } +} From 43a4e1e53d7a5bdb7ac769464d3a47bf2290e5c0 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 22:26:36 +0900 Subject: [PATCH 16/28] =?UTF-8?q?test:=20LottoNumber=EC=99=80=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=EB=90=98=EB=8A=94=20Lotto=20=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java index 883b2885f..d2e2f67df 100644 --- a/src/test/java/domain/LottoTest.java +++ b/src/test/java/domain/LottoTest.java @@ -20,16 +20,6 @@ void lottoNumbersShouldNotContainsDuplicates() { assertThat(lotto.getNumbers()).doesNotHaveDuplicates(); } - @Test - @DisplayName("로또 번호는 1부터 45 사이이다") - void lottoNumbersShouldBeBetweenOneAndFortyFive() { - Lotto lotto = new Lotto(); - - assertThat(lotto.getNumbers()) - .allMatch(lottoNumber -> lottoNumber.getNumber() >= 1 - && lottoNumber.getNumber() <= 45); - } - @Test @DisplayName("로또는 6개의 번호를 가진다") void lottoShouldContainsSizNumbers() { From 59618e0d2b03337bc9d5a587fb61072d7947ac96 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 22:28:51 +0900 Subject: [PATCH 17/28] =?UTF-8?q?docs:=20=EB=A1=9C=EB=98=90=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=20README=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..474631395 --- /dev/null +++ b/README.md @@ -0,0 +1,146 @@ +# 로또 - 클린 코드 + +## 미션 소개 + +로또 구매 금액과 수동 구매 번호를 입력받아 로또를 발급하고, +지난주 당첨 번호와 보너스 번호를 기준으로 당첨 결과와 수익률을 계산하는 프로그램 + +--- + +## 요구사항 + +- 로또 한 장의 가격은 1,000원이다. +- 로또 번호는 1부터 45 사이의 숫자이다. +- 하나의 로또는 서로 다른 6개의 번호로 구성된다. +- 구입 금액에 따라 구매 가능한 로또 개수를 계산한다. +- 사용자가 원하는 개수만큼 로또 번호를 수동으로 입력할 수 있다. +- 수동으로 구매하고 남은 개수는 자동으로 생성한다. +- 자동 로또 번호는 중복되지 않는 6개의 숫자로 생성한다. +- 지난주 당첨 번호 6개와 보너스 번호 1개를 입력받는다. +- 당첨 번호와 보너스 번호는 중복될 수 없다. +- 당첨 번호의 일치 개수와 보너스 번호 일치 여부에 따라 등수를 결정한다. +- 당첨 결과를 등수별로 집계한다. +- 총 당첨 금액을 기준으로 수익률을 계산한다. +- Java Enum을 활용하여 로또 등수와 상금을 관리한다. +- 메서드의 길이는 최대한 10라인을 넘지 않도록 구현한다. +- 들여쓰기 depth는 1을 넘지 않도록 구현한다. + +### 추가 구현 기준 +- 최소 구입 금액은 로또 1장 가격과 동일한 1,000원으로 정한다. +- 1,000원 미만의 금액을 입력하면 예외가 발생한다. + +--- + +## 기능 목록 + +### 로또 구입 + +- [x] 구입 금액 입력 +- [x] 최소 구입 금액 검증 +- [x] 구입 금액에 따른 총 구매 가능 개수 계산 +- [x] 수동 구매 개수 입력 +- [x] 수동 구매 개수가 음수인지 검증 +- [x] 수동 구매 개수가 전체 구매 가능 개수를 초과하는지 검증 +- [x] 수동 구매 개수를 제외한 자동 구매 개수 계산 + +### 로또 생성 + +- [x] 수동 로또 번호 입력 +- [x] 수동 로또 번호 개수 검증 +- [x] 수동 로또 번호 중복 검증 +- [x] 자동 로또 번호 생성 +- [x] 로또 번호 범위 검증 +- [x] 로또 번호 정렬 +- [x] 수동 로또와 자동 로또를 하나의 컬렉션으로 관리 + +### 당첨 번호 + +- [x] 지난주 당첨 번호 입력 +- [x] 당첨 번호 개수 검증 +- [x] 당첨 번호 중복 검증 +- [x] 보너스 번호 입력 +- [x] 보너스 번호와 당첨 번호의 중복 검증 + +### 당첨 결과 + +- [x] 로또와 당첨 번호의 일치 개수 계산 +- [x] 보너스 번호 일치 여부 확인 +- [x] 일치 개수와 보너스 번호에 따른 당첨 등수 결정 +- [x] 등수별 당첨 개수 집계 +- [x] 총 당첨 금액 계산 +- [x] 수익률 계산 +- [x] 당첨 통계 출력 + +--- + +## 당첨 기준 + +| 등수 | 당첨 기준 | 상금 | +| --- | --- | ---: | +| 1등 | 6개 번호 일치 | 2,000,000,000원 | +| 2등 | 5개 번호 + 보너스 번호 일치 | 30,000,000원 | +| 3등 | 5개 번호 일치 | 1,500,000원 | +| 4등 | 4개 번호 일치 | 50,000원 | +| 5등 | 3개 번호 일치 | 5,000원 | + +--- + +## 주요 객체 + +| 객체 | 역할 | +| --- | --- | +| `PurchaseAmount` | 구입 금액을 관리하고 구매 가능한 로또 개수를 계산한다. | +| `LottoNumber` | 로또 번호 하나를 관리하고 번호의 범위를 검증한다. | +| `Lotto` | 6개의 로또 번호를 관리하고 자동 또는 수동으로 로또를 생성한다. | +| `Lottos` | 여러 개의 로또를 관리한다. | +| `WinningNumbers` | 당첨 번호를 관리하고 로또와 일치하는 번호 개수를 계산한다. | +| `LottoRank` | 당첨 등수와 상금을 관리한다. | +| `LottoStatistics` | 등수별 당첨 결과와 수익률을 계산한다. | +| `InputView` | 사용자 입력을 담당한다. | +| `OutputView` | 로또 및 당첨 결과 출력을 담당한다. | + +--- + +## 테스트 + +### PurchaseAmountTest + +- 구입 금액에 따른 로또 구매 개수 계산 +- 최소 구입 금액 검증 +- 수동 구매 개수가 음수인 경우 검증 +- 수동 구매 개수가 구매 가능 개수를 초과하는 경우 검증 +- 수동 구매 개수를 제외한 자동 구매 개수 계산 +- 모든 로또를 수동으로 구매한 경우 자동 구매 개수가 0인지 확인 + +### LottoNumberTest + +- 로또 번호의 최소/최대 범위 확인 +- 로또 번호가 유효 범위를 벗어난 경우 예외 확인 + +### LottoTest + +- 로또가 6개의 번호로 생성되는지 확인 +- 로또 번호 중복 여부 확인 +- 직접 지정한 번호가 6개가 아닌 경우 예외 확인 + +### LottosTest + +- 수동 로또와 자동 로또를 합한 개수만큼 로또가 생성되는지 확인 +- 수동 구매가 없는 경우 자동 로또만 생성되는지 확인 + +### WinningNumbersTest + +- 당첨 번호가 6개인지 확인 +- 당첨 번호 중복 검증 +- 보너스 번호와 당첨 번호 중복 검증 + +### LottoRankTest + +- 일치 개수와 보너스 번호 일치 여부에 따라 올바른 당첨 등수를 반환하는지 확인 +- 당첨 기준에 미달하면 당첨되지 않는지 확인 + +### LottoStatisticsTest + +- 실제 로또의 당첨 결과를 등수별로 올바르게 집계하는지 확인 + + From 212d96c845c8fb731a3d231d8f3cf472a1a87e8b Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sun, 2 Aug 2026 22:39:53 +0900 Subject: [PATCH 18/28] =?UTF-8?q?test:=20=EB=A1=9C=EB=98=90=20=EC=88=98?= =?UTF-8?q?=EC=9D=B5=EB=A5=A0=20=EA=B3=84=EC=82=B0=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoStatisticsTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/domain/LottoStatisticsTest.java b/src/test/java/domain/LottoStatisticsTest.java index 0be2e6000..636561846 100644 --- a/src/test/java/domain/LottoStatisticsTest.java +++ b/src/test/java/domain/LottoStatisticsTest.java @@ -32,4 +32,27 @@ void calculatesWinningCountForEachRank() { assertEquals(1, lottoStatistics.getWinningCount(LottoRank.FOURTH)); assertEquals(1, lottoStatistics.getWinningCount(LottoRank.FIFTH)); } + + @Test + @DisplayName("총 당첨 금액과 구입 금액을 기준으로 수익률을 계산한다") + void calculatesProfitRateBasedOnTotalPrize() { + Lottos lottos = new Lottos(createLottoNumbersForProfitRate(), 0); + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6)); + LottoNumber bonusNumber = new LottoNumber(7); + PurchaseAmount purchaseAmount = new PurchaseAmount(5000); + + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); + + assertEquals(1.0, lottoStatistics.calculateProfitRate(purchaseAmount), 0.0001); + } + + private List> createLottoNumbersForProfitRate() { + return List.of( + List.of(1, 2, 3, 8, 9, 10), + List.of(1, 2, 8, 9, 10, 11), + List.of(1, 8, 9, 10, 11, 12), + List.of(8, 9, 10, 11, 12, 13), + List.of(14, 15, 16, 17, 18, 19) + ); + } } From 3d834f656b8623720a1c306911a472b1bad84500 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 14:04:45 +0900 Subject: [PATCH 19/28] =?UTF-8?q?fix:=20=EA=B5=AC=EC=9E=85=20=EA=B8=88?= =?UTF-8?q?=EC=95=A1=EC=9D=84=201000=EC=9B=90=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/PurchaseAmount.java | 11 +++++++++-- src/test/java/domain/PurchaseAmountTest.java | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java index e6a18598d..8e2e0e676 100644 --- a/src/main/java/domain/PurchaseAmount.java +++ b/src/main/java/domain/PurchaseAmount.java @@ -7,7 +7,8 @@ public class PurchaseAmount { private final int amount; public PurchaseAmount(int amount) { - validatePurchaseAmount(amount); + validateMinimumPurchaseAmount(amount); + validatePurchaseAmountUnit(amount); this.amount = amount; } @@ -36,9 +37,15 @@ private void validateManualLottoCount(int manualLottoCount) { } } - private void validatePurchaseAmount(int amount) { + private void validateMinimumPurchaseAmount(int amount) { if (amount < LOTTO_PRICE) { throw new IllegalArgumentException("최소 구입 금액은 " + LOTTO_PRICE + "원 입니다."); } } + + private void validatePurchaseAmountUnit(int amount) { + if (amount % LOTTO_PRICE != 0) { + throw new IllegalArgumentException("구입 금액은 1,000원 단위여야 합니다."); + } + } } diff --git a/src/test/java/domain/PurchaseAmountTest.java b/src/test/java/domain/PurchaseAmountTest.java index 7730161d8..697d6b95d 100644 --- a/src/test/java/domain/PurchaseAmountTest.java +++ b/src/test/java/domain/PurchaseAmountTest.java @@ -35,6 +35,15 @@ void throwsExceptionWhenPurchaseAmountIsLessThanLottoPrice(int amount) { ); } + @Test + @DisplayName("구입 금액이 로또 가격 단위가 아니면 예외가 발생한다") + void throwsExceptionWhenPurchaseAMountIsNotMultipleOfLottoPrice() { + assertThrows( + IllegalArgumentException.class, + () -> new PurchaseAmount(1500) + ); + } + @Test @DisplayName("수동 구매 개수가 음수이면 예외가 발생한다") void throwsExceptionWhenManualLottoCountIsNegative() { From 87274037127e162ade8f79f1e63877734698210d Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 14:20:38 +0900 Subject: [PATCH 20/28] =?UTF-8?q?refactor:=20LottoStatistics=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=B1=85=EC=9E=84=EC=9D=84=20Application=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 4 +++- src/main/java/view/OutputView.java | 5 +---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 5c11d8002..45814b652 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,4 +1,5 @@ import domain.LottoNumber; +import domain.LottoStatistics; import domain.Lottos; import domain.PurchaseAmount; import domain.WinningNumbers; @@ -30,6 +31,7 @@ public static void main(String[] args) { winningNumbers.validateBonusNumber(bonusNumber); - outputView.printLottoStatistics(lottos, winningNumbers, purchaseAmount, bonusNumber); + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); + outputView.printLottoStatistics(purchaseAmount, lottoStatistics); } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 157306205..bcaad16c1 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -6,7 +6,6 @@ import domain.LottoStatistics; import domain.Lottos; import domain.PurchaseAmount; -import domain.WinningNumbers; import java.util.ArrayList; import java.util.List; @@ -23,11 +22,9 @@ public void printLottos(Lottos lottos) { System.out.println(); } - public void printLottoStatistics(Lottos lottos, WinningNumbers winningNumbers, - PurchaseAmount purchaseAmount, LottoNumber bonusNumber) { + public void printLottoStatistics(PurchaseAmount purchaseAmount, LottoStatistics lottoStatistics) { printStatisticsHeader(); - LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); printWinningCounts(lottoStatistics); printProfitRate(lottoStatistics, purchaseAmount); From 14aae3aa303b5aaf578d4d19298fb674b25ff77e Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 14:37:08 +0900 Subject: [PATCH 21/28] =?UTF-8?q?test:=20WinningNumbers=20=EC=A0=95?= =?UTF-8?q?=EC=83=81=20=EC=83=9D=EC=84=B1=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9D=98=EB=8F=84=20=EB=AA=85=ED=99=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/WinningNumbersTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/domain/WinningNumbersTest.java b/src/test/java/domain/WinningNumbersTest.java index 67d22f5e4..bc635af88 100644 --- a/src/test/java/domain/WinningNumbersTest.java +++ b/src/test/java/domain/WinningNumbersTest.java @@ -5,6 +5,7 @@ import java.util.List; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; public class WinningNumbersTest { @@ -12,7 +13,8 @@ public class WinningNumbersTest { @DisplayName("당첨 번호가 정확히 6개면 생성된다") void createsWinningNumbersWithSixNumbers() { List numbers = List.of(1, 2, 3, 4, 5, 6); - WinningNumbers winningNumbers = new WinningNumbers(numbers); + + assertDoesNotThrow(() -> new WinningNumbers(numbers)); } @Test From d9735986aab6d7afcaebe0756889386bf96e22be Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 15:50:07 +0900 Subject: [PATCH 22/28] =?UTF-8?q?refactor:=20LottoRank=EC=97=90=20MISS=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/LottoRank.java | 12 +++++++++++- src/main/java/domain/LottoStatistics.java | 4 ---- src/main/java/view/OutputView.java | 2 +- src/test/java/domain/LottoRankTest.java | 3 +-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/java/domain/LottoRank.java b/src/main/java/domain/LottoRank.java index 336f2fb88..c5674925a 100644 --- a/src/main/java/domain/LottoRank.java +++ b/src/main/java/domain/LottoRank.java @@ -1,6 +1,10 @@ package domain; +import java.util.Arrays; +import java.util.List; + public enum LottoRank { + MISS(0,0), FIFTH(3, 5000), FOURTH(4,50000), THIRD(5, 1500000), @@ -21,7 +25,7 @@ public static LottoRank from(int matchCount, boolean bonusMatched) { if (matchCount == 5) return THIRD; if (matchCount == 4) return FOURTH; if (matchCount == 3) return FIFTH; - return null; + return MISS; } public int getMatchCount() { @@ -31,4 +35,10 @@ public int getMatchCount() { public long getPrize() { return prize; } + + public static List winningRanks() { + return Arrays.stream(values()) + .filter(rank -> rank != MISS) + .toList(); + } } diff --git a/src/main/java/domain/LottoStatistics.java b/src/main/java/domain/LottoStatistics.java index 168cba135..50db75c94 100644 --- a/src/main/java/domain/LottoStatistics.java +++ b/src/main/java/domain/LottoStatistics.java @@ -56,10 +56,6 @@ private void processLottoResult(Lotto lotto, WinningNumbers winningNumbers, Lott LottoRank rank = LottoRank.from(matchCount, bonusMatched); - if (rank == null) { - return; - } - increaseWinningCount(rank); } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index bcaad16c1..ce0c0adff 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -46,7 +46,7 @@ private void printStatisticsHeader() { } private void printWinningCounts(LottoStatistics lottoStatistics) { - for (LottoRank rank : LottoRank.values()) { + for (LottoRank rank : LottoRank.winningRanks()) { printWinningResult(lottoStatistics, rank); } } diff --git a/src/test/java/domain/LottoRankTest.java b/src/test/java/domain/LottoRankTest.java index 52c6cccea..2d3344f2f 100644 --- a/src/test/java/domain/LottoRankTest.java +++ b/src/test/java/domain/LottoRankTest.java @@ -1,7 +1,6 @@ package domain; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -52,6 +51,6 @@ void returnsFifthWhenThreeNumbersMatch() { void returnsNullWhenMatchCountIsLessThanThree() { LottoRank rank = LottoRank.from(2, false); - assertNull(rank); + assertEquals(LottoRank.MISS, rank); } } From 4e1590b45c54d134703738fd5e74a504d43d7d0b Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 16:34:37 +0900 Subject: [PATCH 23/28] =?UTF-8?q?refactor:=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EB=A5=BC=20WinningLotto=EB=A1=9C=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 5 ++-- src/main/java/domain/LottoStatistics.java | 19 ++++-------- src/main/java/domain/WinningLotto.java | 30 +++++++++++++++++++ src/main/java/domain/WinningNumbers.java | 6 ++-- src/test/java/domain/LottoStatisticsTest.java | 6 ++-- src/test/java/domain/WinningLottoTest.java | 23 ++++++++++++++ src/test/java/domain/WinningNumbersTest.java | 14 --------- 7 files changed, 68 insertions(+), 35 deletions(-) create mode 100644 src/main/java/domain/WinningLotto.java create mode 100644 src/test/java/domain/WinningLottoTest.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 45814b652..a4dfa12fe 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -2,6 +2,7 @@ import domain.LottoStatistics; import domain.Lottos; import domain.PurchaseAmount; +import domain.WinningLotto; import domain.WinningNumbers; import view.InputView; @@ -29,9 +30,9 @@ public static void main(String[] args) { WinningNumbers winningNumbers = new WinningNumbers(inputView.readWinningNumbers()); LottoNumber bonusNumber = new LottoNumber(inputView.readBonusNumber()); - winningNumbers.validateBonusNumber(bonusNumber); + WinningLotto winningLotto = new WinningLotto(winningNumbers, bonusNumber); - LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningLotto); outputView.printLottoStatistics(purchaseAmount, lottoStatistics); } } diff --git a/src/main/java/domain/LottoStatistics.java b/src/main/java/domain/LottoStatistics.java index 50db75c94..31ab90854 100644 --- a/src/main/java/domain/LottoStatistics.java +++ b/src/main/java/domain/LottoStatistics.java @@ -6,10 +6,10 @@ public class LottoStatistics { private final Map winningCounts = new EnumMap<>(LottoRank.class); - public LottoStatistics(Lottos lottos, WinningNumbers winningNumbers, LottoNumber bonusNumber) { + public LottoStatistics(Lottos lottos, WinningLotto winningLotto) { initializeWinningCounts(); - calculateWinningCounts(lottos, winningNumbers, bonusNumber); + calculateWinningCounts(lottos, winningLotto); } public int getWinningCount(LottoRank rank) { @@ -36,9 +36,9 @@ private void initializeWinningCounts() { } } - private void calculateWinningCounts(Lottos lottos, WinningNumbers winningNumbers, LottoNumber bonusNumber) { + private void calculateWinningCounts(Lottos lottos, WinningLotto winningLotto) { for (Lotto lotto : lottos.getLottos()) { - processLottoResult(lotto, winningNumbers, bonusNumber); + processLottoResult(lotto, winningLotto); } } @@ -46,15 +46,8 @@ private void increaseWinningCount(LottoRank rank) { winningCounts.put(rank, winningCounts.get(rank) + 1); } - private boolean isBonusNumberMatched(Lotto lotto, LottoNumber bonusNumber) { - return lotto.getNumbers().contains(bonusNumber); - } - - private void processLottoResult(Lotto lotto, WinningNumbers winningNumbers, LottoNumber bonusNumber) { - int matchCount = winningNumbers.countMatchingNumbers(lotto); - boolean bonusMatched = isBonusNumberMatched(lotto, bonusNumber); - - LottoRank rank = LottoRank.from(matchCount, bonusMatched); + private void processLottoResult(Lotto lotto, WinningLotto winningLotto) { + LottoRank rank = winningLotto.determineRank(lotto); increaseWinningCount(rank); } diff --git a/src/main/java/domain/WinningLotto.java b/src/main/java/domain/WinningLotto.java new file mode 100644 index 000000000..9ff0f1ed9 --- /dev/null +++ b/src/main/java/domain/WinningLotto.java @@ -0,0 +1,30 @@ +package domain; + +public class WinningLotto { + private final WinningNumbers winningNumbers; + private final LottoNumber bonusNumber; + + public WinningLotto(WinningNumbers winningNumbers, LottoNumber bonusNumber) { + validateBonusNumber(winningNumbers, bonusNumber); + + this.winningNumbers = winningNumbers; + this.bonusNumber = bonusNumber; + } + + public LottoRank determineRank(Lotto lotto) { + int matchCount = winningNumbers.countMatchingNumbers(lotto); + boolean bonusMatched = isBonusNumberMatched(lotto); + + return LottoRank.from(matchCount, bonusMatched); + } + + private boolean isBonusNumberMatched(Lotto lotto) { + return lotto.getNumbers().contains(bonusNumber); + } + + private void validateBonusNumber(WinningNumbers winningNumbers, LottoNumber bonusNumber) { + if (winningNumbers.contains(bonusNumber)) { + throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); + } + } +} diff --git a/src/main/java/domain/WinningNumbers.java b/src/main/java/domain/WinningNumbers.java index ce524c249..e6292c064 100644 --- a/src/main/java/domain/WinningNumbers.java +++ b/src/main/java/domain/WinningNumbers.java @@ -19,10 +19,8 @@ public WinningNumbers(List numbers) { winningNumbers.addAll(lottoNumbers); } - public void validateBonusNumber(LottoNumber bonusNumber) { - if (winningNumbers.contains(bonusNumber)) { - throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); - } + public boolean contains(LottoNumber number) { + return winningNumbers.contains(number); } int countMatchingNumbers(Lotto lotto) { diff --git a/src/test/java/domain/LottoStatisticsTest.java b/src/test/java/domain/LottoStatisticsTest.java index 636561846..6e8c3952a 100644 --- a/src/test/java/domain/LottoStatisticsTest.java +++ b/src/test/java/domain/LottoStatisticsTest.java @@ -14,6 +14,7 @@ void calculatesWinningCountForEachRank() { List winningNumberValues = List.of(1, 2, 3, 4, 5, 6); WinningNumbers winningNumbers = new WinningNumbers(winningNumberValues); LottoNumber bonusNumber = new LottoNumber(7); + WinningLotto winningLotto = new WinningLotto(winningNumbers, bonusNumber); List> lottoNumbers = List.of( List.of(1, 2, 3, 4, 5, 6), @@ -24,7 +25,7 @@ void calculatesWinningCountForEachRank() { ); Lottos lottos = new Lottos(lottoNumbers, 0); - LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningLotto); assertEquals(1, lottoStatistics.getWinningCount(LottoRank.FIRST)); assertEquals(1, lottoStatistics.getWinningCount(LottoRank.SECOND)); @@ -39,9 +40,10 @@ void calculatesProfitRateBasedOnTotalPrize() { Lottos lottos = new Lottos(createLottoNumbersForProfitRate(), 0); WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6)); LottoNumber bonusNumber = new LottoNumber(7); + WinningLotto winningLotto = new WinningLotto(winningNumbers, bonusNumber); PurchaseAmount purchaseAmount = new PurchaseAmount(5000); - LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningNumbers, bonusNumber); + LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningLotto); assertEquals(1.0, lottoStatistics.calculateProfitRate(purchaseAmount), 0.0001); } diff --git a/src/test/java/domain/WinningLottoTest.java b/src/test/java/domain/WinningLottoTest.java new file mode 100644 index 000000000..715bbd160 --- /dev/null +++ b/src/test/java/domain/WinningLottoTest.java @@ -0,0 +1,23 @@ +package domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class WinningLottoTest { + @Test + @DisplayName("보너스 번호가 당첨 번호와 중복되면 예외가 발생한다") + void throwsExceptionWhenBonusNumberIsDuplicatedWithWinningNumbers() { + List numbers = List.of(1, 2, 3, 4, 5, 6); + LottoNumber bonusNumber = new LottoNumber(3); + WinningNumbers winningNumbers = new WinningNumbers(numbers); + + assertThrows( + IllegalArgumentException.class, + () -> new WinningLotto(winningNumbers, bonusNumber) + ); + } +} diff --git a/src/test/java/domain/WinningNumbersTest.java b/src/test/java/domain/WinningNumbersTest.java index bc635af88..1615e9a10 100644 --- a/src/test/java/domain/WinningNumbersTest.java +++ b/src/test/java/domain/WinningNumbersTest.java @@ -38,18 +38,4 @@ void throwsExceptionWhenWinningNumbersContainDuplicates() { () -> new WinningNumbers(numbers) ); } - - @Test - @DisplayName("보너스 번호가 당첨 번호와 중복되면 예외가 발생한다") - void throwsExceptionWhenBonusNumberIsDuplicatedWithWinningNumbers() { - List numbers = List.of(1, 2, 3, 4, 5, 6); - LottoNumber bonusNumber = new LottoNumber(3); - - WinningNumbers winningNumbers = new WinningNumbers(numbers); - - assertThrows( - IllegalArgumentException.class, - () -> winningNumbers.validateBonusNumber(bonusNumber) - ); - } } From a100ceb9668d70bb1ceee27f4b6824345077013b Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 17:38:35 +0900 Subject: [PATCH 24/28] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EC=83=9D=EC=84=B1=20=EC=B1=85=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20LottoGenerator=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 29 -------------- src/main/java/domain/LottoGenerator.java | 40 ++++++++++++++++++++ src/main/java/domain/Lottos.java | 3 +- src/test/java/domain/LottoGeneratorTest.java | 26 +++++++++++++ src/test/java/domain/LottoTest.java | 20 ++++------ 5 files changed, 75 insertions(+), 43 deletions(-) create mode 100644 src/main/java/domain/LottoGenerator.java create mode 100644 src/test/java/domain/LottoGeneratorTest.java diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 348811e75..ca3f84cff 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -4,19 +4,12 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Random; public class Lotto { private static final int NUMBER_COUNT = 6; - private static final int RANDOM_NUMBER_BOUND = 45; - private final Random random = new Random(); private final List numbers = new ArrayList<>(); - public Lotto() { - makeNumbers(); - } - public Lotto(List numberValues) { validateNumberCount(numberValues); validateDuplicateNumbers(numberValues); @@ -30,28 +23,6 @@ public List getNumbers() { return List.copyOf(numbers); } - private void makeNumbers() { - for (int i = 0; i < NUMBER_COUNT; i++) { - numbers.add(generateRandomNumber()); - } - - Collections.sort(numbers); - } - - private LottoNumber generateRandomNumber() { - LottoNumber lottoNumber; - - do { - lottoNumber = new LottoNumber(random.nextInt(RANDOM_NUMBER_BOUND) + 1); - } while (containsNumber(lottoNumber)); - - return lottoNumber; - } - - private boolean containsNumber(LottoNumber lottoNumber) { - return numbers.contains(lottoNumber); - } - private void convertToLottoNumbers(List numberValues) { for (int number : numberValues) { LottoNumber lottoNumber = new LottoNumber(number); diff --git a/src/main/java/domain/LottoGenerator.java b/src/main/java/domain/LottoGenerator.java new file mode 100644 index 000000000..862e8cd65 --- /dev/null +++ b/src/main/java/domain/LottoGenerator.java @@ -0,0 +1,40 @@ +package domain; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class LottoGenerator { + private static final int NUMBER_COUNT = 6; + private static final int RANDOM_NUMBER_BOUND = 45; + + private final Random random = new Random(); + + public Lotto generate() { + return new Lotto(generateNumbers()); + } + + private List generateNumbers() { + List numbers = new ArrayList<>(); + + for (int i = 0; i < NUMBER_COUNT; i++) { + numbers.add(generateRandomNumber(numbers)); + } + + return numbers; + } + + private int generateRandomNumber(List numbers) { + int number; + + do { + number = random.nextInt(RANDOM_NUMBER_BOUND) + 1; + } while (containsNumber(numbers, number)); + + return number; + } + + private boolean containsNumber(List numbers, int number) { + return numbers.contains(number); + } +} diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 3e1d84552..1bcf7afd8 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -5,6 +5,7 @@ public class Lottos { private final List lottos = new ArrayList<>(); + private final LottoGenerator lottoGenerator = new LottoGenerator(); public Lottos(List> manualLottoNumbers, int autoLottoCount) { for (List manualLottoNumber : manualLottoNumbers) { @@ -12,7 +13,7 @@ public Lottos(List> manualLottoNumbers, int autoLottoCount) { } for (int i = 0; i < autoLottoCount; i++) { - lottos.add(new Lotto()); + lottos.add(lottoGenerator.generate()); } } diff --git a/src/test/java/domain/LottoGeneratorTest.java b/src/test/java/domain/LottoGeneratorTest.java new file mode 100644 index 000000000..61207aa4c --- /dev/null +++ b/src/test/java/domain/LottoGeneratorTest.java @@ -0,0 +1,26 @@ +package domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class LottoGeneratorTest { + @Test + @DisplayName("자동 생성된 로또 번호는 중복되지 않는다") + void lottoNumbersShouldNotContainDuplicates() { + LottoGenerator lottoGenerator = new LottoGenerator(); + Lotto lotto = lottoGenerator.generate(); + + assertThat(lotto.getNumbers()).doesNotHaveDuplicates(); + } + + @Test + @DisplayName("자동 생성된 로또는 6개의 번호를 가진다") + void lottoShouldContainsSixNumbers() { + LottoGenerator lottoGenerator = new LottoGenerator(); + Lotto lotto = lottoGenerator.generate(); + + assertThat(lotto.getNumbers()).hasSize(6); + } +} diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java index d2e2f67df..5a12a2daa 100644 --- a/src/test/java/domain/LottoTest.java +++ b/src/test/java/domain/LottoTest.java @@ -1,6 +1,5 @@ package domain; -import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.DisplayName; @@ -13,19 +12,14 @@ public class LottoTest { @Test - @DisplayName("로또 번호는 중복되지 않는다") - void lottoNumbersShouldNotContainsDuplicates() { - Lotto lotto = new Lotto(); + @DisplayName("로또 번호가 중복되면 예외가 발생한다") + void throwsExceptionWhenLottoNumbersContainDuplicates() { + List numbers = List.of(1, 2, 3, 4, 5, 5); - assertThat(lotto.getNumbers()).doesNotHaveDuplicates(); - } - - @Test - @DisplayName("로또는 6개의 번호를 가진다") - void lottoShouldContainsSizNumbers() { - Lotto lotto = new Lotto(); - - assertThat(lotto.getNumbers()).hasSize(6); + assertThrows( + IllegalArgumentException.class, + () -> new Lotto(numbers) + ); } @ParameterizedTest From a3c530484b73c93d2bfff2c92a63e2c7e785fae9 Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 21:11:41 +0900 Subject: [PATCH 25/28] =?UTF-8?q?test:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=EC=9D=98=20=EA=B5=AC=EB=A7=A4=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8=20=EC=97=AC=EB=B6=80=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottosTest.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/test/java/domain/LottosTest.java b/src/test/java/domain/LottosTest.java index ac7d10c1b..78483886f 100644 --- a/src/test/java/domain/LottosTest.java +++ b/src/test/java/domain/LottosTest.java @@ -1,6 +1,7 @@ package domain; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -9,7 +10,7 @@ public class LottosTest { @Test - @DisplayName("수동 로또와 자동 로또를 합한 개수만큼 로또를 생성한다") + @DisplayName("수동 로또를 포함하여 구매 개수만큼 로또를 생성한다") void createsManualAndAutoLottos() { PurchaseAmount purchaseAmount = new PurchaseAmount(12000); @@ -24,10 +25,22 @@ void createsManualAndAutoLottos() { Lottos lottos = new Lottos(manualLottoNumbers, autoLottoCount); assertEquals(12, lottos.getLottos().size()); + assertContainsManualLottos(lottos, manualLottoNumbers); + } + + private void assertContainsManualLottos(Lottos lottos, List> manualLottoNumbers) { + List> expectedManualLottos = manualLottoNumbers.stream() + .map(Lotto::new) + .map(Lotto::getNumbers) + .toList(); + + assertThat(lottos.getLottos()) + .extracting(Lotto::getNumbers) + .containsAll(expectedManualLottos); } @Test - @DisplayName("수동 로또가 없으면 자동 로또만 생성한다") + @DisplayName("수동 로또가 없으면 자동 구매 개수만큼 로또를 생성한다") void createsOnlyAutoLottosWhenThereAreNoManualLottos() { PurchaseAmount purchaseAmount = new PurchaseAmount(14000); From ce392275634f3bd17b3240f448aabdd1bff9f26a Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 21:24:43 +0900 Subject: [PATCH 26/28] =?UTF-8?q?refactor:=20Application=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=20=ED=9D=90=EB=A6=84=EC=9D=84=20=EC=9D=98=EB=AF=B8=20?= =?UTF-8?q?=EB=8B=A8=EC=9C=84=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index a4dfa12fe..9106b4622 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -17,6 +17,14 @@ public static void main(String[] args) { PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readPurchaseAmount()); + Lottos lottos = purchaseLottos(inputView, outputView, purchaseAmount); + + WinningLotto winningLotto = createWinningLotto(inputView); + + printStatistics(outputView, lottos, winningLotto, purchaseAmount); + } + + private static Lottos purchaseLottos(InputView inputView, OutputView outputView, PurchaseAmount purchaseAmount) { int manualLottoCount = inputView.readManualLottoCount(); int autoLottoCount = purchaseAmount.calculateAutoLottoCount(manualLottoCount); @@ -27,11 +35,18 @@ public static void main(String[] args) { outputView.printPurchasedCount(manualLottoCount, autoLottoCount); outputView.printLottos(lottos); + return lottos; + } + + private static WinningLotto createWinningLotto(InputView inputView) { WinningNumbers winningNumbers = new WinningNumbers(inputView.readWinningNumbers()); LottoNumber bonusNumber = new LottoNumber(inputView.readBonusNumber()); - WinningLotto winningLotto = new WinningLotto(winningNumbers, bonusNumber); + return new WinningLotto(winningNumbers, bonusNumber); + } + private static void printStatistics(OutputView outputView, Lottos lottos, + WinningLotto winningLotto, PurchaseAmount purchaseAmount) { LottoStatistics lottoStatistics = new LottoStatistics(lottos, winningLotto); outputView.printLottoStatistics(purchaseAmount, lottoStatistics); } From 8730e7c4541ddd7581431cfc6d59e90d5af563dc Mon Sep 17 00:00:00 2001 From: johaeunn Date: Wed, 5 Aug 2026 21:33:04 +0900 Subject: [PATCH 27/28] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=B0=B0=EC=B9=98=20=EC=88=9C=EC=84=9C=EB=A5=BC=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20=ED=9D=90=EB=A6=84=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/LottoStatistics.java | 8 ++++---- src/main/java/view/OutputView.java | 24 +++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/domain/LottoStatistics.java b/src/main/java/domain/LottoStatistics.java index 31ab90854..63430a8e8 100644 --- a/src/main/java/domain/LottoStatistics.java +++ b/src/main/java/domain/LottoStatistics.java @@ -42,13 +42,13 @@ private void calculateWinningCounts(Lottos lottos, WinningLotto winningLotto) { } } - private void increaseWinningCount(LottoRank rank) { - winningCounts.put(rank, winningCounts.get(rank) + 1); - } - private void processLottoResult(Lotto lotto, WinningLotto winningLotto) { LottoRank rank = winningLotto.determineRank(lotto); increaseWinningCount(rank); } + + private void increaseWinningCount(LottoRank rank) { + winningCounts.put(rank, winningCounts.get(rank) + 1); + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index ce0c0adff..0e5338ffb 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -22,14 +22,6 @@ public void printLottos(Lottos lottos) { System.out.println(); } - public void printLottoStatistics(PurchaseAmount purchaseAmount, LottoStatistics lottoStatistics) { - printStatisticsHeader(); - - printWinningCounts(lottoStatistics); - - printProfitRate(lottoStatistics, purchaseAmount); - } - private void printLotto(Lotto lotto) { List numberValues = new ArrayList<>(); @@ -40,6 +32,14 @@ private void printLotto(Lotto lotto) { System.out.println(numberValues); } + public void printLottoStatistics(PurchaseAmount purchaseAmount, LottoStatistics lottoStatistics) { + printStatisticsHeader(); + + printWinningCounts(lottoStatistics); + + printProfitRate(lottoStatistics, purchaseAmount); + } + private void printStatisticsHeader() { System.out.println("당첨 통계"); System.out.println("---------"); @@ -51,10 +51,6 @@ private void printWinningCounts(LottoStatistics lottoStatistics) { } } - private void printProfitRate(LottoStatistics lottoStatistics, PurchaseAmount purchaseAmount) { - System.out.println("총 수익률은 " + lottoStatistics.calculateProfitRate(purchaseAmount) + "입니다."); - } - private void printWinningResult(LottoStatistics lottoStatistics, LottoRank rank) { if (rank == LottoRank.SECOND) { System.out.println(rank.getMatchCount() + "개 일치, 보너스 볼 일치(" @@ -66,4 +62,8 @@ private void printWinningResult(LottoStatistics lottoStatistics, LottoRank rank) + rank.getPrize() + "원)- " + lottoStatistics.getWinningCount(rank) + "개"); } + + private void printProfitRate(LottoStatistics lottoStatistics, PurchaseAmount purchaseAmount) { + System.out.println("총 수익률은 " + lottoStatistics.calculateProfitRate(purchaseAmount) + "입니다."); + } } From f19affd0ca9df2ab0f0f45cd11793306134d480e Mon Sep 17 00:00:00 2001 From: johaeunn Date: Sat, 8 Aug 2026 01:27:24 +0900 Subject: [PATCH 28/28] =?UTF-8?q?refactor:=20WinningNumbers=EB=A5=BC=20Lot?= =?UTF-8?q?to=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 4 +- src/main/java/domain/Lotto.java | 22 ++++++ src/main/java/domain/WinningLotto.java | 14 ++-- src/main/java/domain/WinningNumbers.java | 69 ------------------- src/test/java/domain/LottoStatisticsTest.java | 4 +- src/test/java/domain/LottoTest.java | 9 +++ src/test/java/domain/WinningLottoTest.java | 2 +- src/test/java/domain/WinningNumbersTest.java | 41 ----------- 8 files changed, 41 insertions(+), 124 deletions(-) delete mode 100644 src/main/java/domain/WinningNumbers.java delete mode 100644 src/test/java/domain/WinningNumbersTest.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 9106b4622..b4a7b8e8c 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,9 +1,9 @@ +import domain.Lotto; import domain.LottoNumber; import domain.LottoStatistics; import domain.Lottos; import domain.PurchaseAmount; import domain.WinningLotto; -import domain.WinningNumbers; import view.InputView; import view.OutputView; @@ -39,7 +39,7 @@ private static Lottos purchaseLottos(InputView inputView, OutputView outputView, } private static WinningLotto createWinningLotto(InputView inputView) { - WinningNumbers winningNumbers = new WinningNumbers(inputView.readWinningNumbers()); + Lotto winningNumbers = new Lotto(inputView.readWinningNumbers()); LottoNumber bonusNumber = new LottoNumber(inputView.readBonusNumber()); return new WinningLotto(winningNumbers, bonusNumber); diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index ca3f84cff..1dc807b85 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -30,6 +30,28 @@ private void convertToLottoNumbers(List numberValues) { } } + public int countMatchingNumbers(Lotto other) { + int count = 0; + + for (LottoNumber lottoNumber : other.numbers) { + count += countMatch(lottoNumber); + } + + return count; + } + + private int countMatch(LottoNumber lottoNumber) { + if (containsNumber(lottoNumber)) { + return 1; + } + + return 0; + } + + public boolean containsNumber(LottoNumber lottoNumber) { + return numbers.contains(lottoNumber); + } + private void validateNumberCount(List numberValues) { if (numberValues.size() != NUMBER_COUNT) { throw new IllegalArgumentException("로또 번호는 " + NUMBER_COUNT + "개여야 합니다."); diff --git a/src/main/java/domain/WinningLotto.java b/src/main/java/domain/WinningLotto.java index 9ff0f1ed9..cafe33af8 100644 --- a/src/main/java/domain/WinningLotto.java +++ b/src/main/java/domain/WinningLotto.java @@ -1,10 +1,10 @@ package domain; public class WinningLotto { - private final WinningNumbers winningNumbers; + private final Lotto winningNumbers; private final LottoNumber bonusNumber; - public WinningLotto(WinningNumbers winningNumbers, LottoNumber bonusNumber) { + public WinningLotto(Lotto winningNumbers, LottoNumber bonusNumber) { validateBonusNumber(winningNumbers, bonusNumber); this.winningNumbers = winningNumbers; @@ -13,17 +13,13 @@ public WinningLotto(WinningNumbers winningNumbers, LottoNumber bonusNumber) { public LottoRank determineRank(Lotto lotto) { int matchCount = winningNumbers.countMatchingNumbers(lotto); - boolean bonusMatched = isBonusNumberMatched(lotto); + boolean bonusMatched = lotto.containsNumber(bonusNumber); return LottoRank.from(matchCount, bonusMatched); } - private boolean isBonusNumberMatched(Lotto lotto) { - return lotto.getNumbers().contains(bonusNumber); - } - - private void validateBonusNumber(WinningNumbers winningNumbers, LottoNumber bonusNumber) { - if (winningNumbers.contains(bonusNumber)) { + private void validateBonusNumber(Lotto winningNumbers, LottoNumber bonusNumber) { + if (winningNumbers.containsNumber(bonusNumber)) { throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); } } diff --git a/src/main/java/domain/WinningNumbers.java b/src/main/java/domain/WinningNumbers.java deleted file mode 100644 index e6292c064..000000000 --- a/src/main/java/domain/WinningNumbers.java +++ /dev/null @@ -1,69 +0,0 @@ -package domain; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -public class WinningNumbers { - private static final int WINNING_NUMBER_COUNT = 6; - - private final List winningNumbers = new ArrayList<>(); - - public WinningNumbers(List numbers) { - validateWinningNumberCount(numbers); - - List lottoNumbers = convertToLottoNumbers(numbers); - - validateDuplicateWinningNumbers(lottoNumbers); - - winningNumbers.addAll(lottoNumbers); - } - - public boolean contains(LottoNumber number) { - return winningNumbers.contains(number); - } - - int countMatchingNumbers(Lotto lotto) { - int count = 0; - - for (LottoNumber lottoNumber : lotto.getNumbers()) { - count += countMatch(lottoNumber); - } - - return count; - } - - private int countMatch(LottoNumber lottoNumber) { - if (isWinningNumber(lottoNumber)) { - return 1; - } - - return 0; - } - - private boolean isWinningNumber(LottoNumber lottoNumber) { - return winningNumbers.contains(lottoNumber); - } - - private List convertToLottoNumbers(List numbers) { - List lottoNumbers = new ArrayList<>(); - - for (int number : numbers) { - lottoNumbers.add(new LottoNumber(number)); - } - - return lottoNumbers; - } - - private void validateWinningNumberCount(List numbers) { - if (numbers.size() != WINNING_NUMBER_COUNT) { - throw new IllegalArgumentException("당첨 번호는 6개여야 합니다."); - } - } - - private void validateDuplicateWinningNumbers(List lottoNumbers) { - if (new HashSet<>(lottoNumbers).size() != lottoNumbers.size()) { - throw new IllegalArgumentException("당첨 번호는 중복될 수 없습니다."); - } - } -} diff --git a/src/test/java/domain/LottoStatisticsTest.java b/src/test/java/domain/LottoStatisticsTest.java index 6e8c3952a..8033b9ed8 100644 --- a/src/test/java/domain/LottoStatisticsTest.java +++ b/src/test/java/domain/LottoStatisticsTest.java @@ -12,7 +12,7 @@ public class LottoStatisticsTest { @DisplayName("로또 당첨 결과를 등수별로 집계한다") void calculatesWinningCountForEachRank() { List winningNumberValues = List.of(1, 2, 3, 4, 5, 6); - WinningNumbers winningNumbers = new WinningNumbers(winningNumberValues); + Lotto winningNumbers = new Lotto(winningNumberValues); LottoNumber bonusNumber = new LottoNumber(7); WinningLotto winningLotto = new WinningLotto(winningNumbers, bonusNumber); @@ -38,7 +38,7 @@ void calculatesWinningCountForEachRank() { @DisplayName("총 당첨 금액과 구입 금액을 기준으로 수익률을 계산한다") void calculatesProfitRateBasedOnTotalPrize() { Lottos lottos = new Lottos(createLottoNumbersForProfitRate(), 0); - WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6)); + Lotto winningNumbers = new Lotto(List.of(1, 2, 3, 4, 5, 6)); LottoNumber bonusNumber = new LottoNumber(7); WinningLotto winningLotto = new WinningLotto(winningNumbers, bonusNumber); PurchaseAmount purchaseAmount = new PurchaseAmount(5000); diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java index 5a12a2daa..53a9bc42c 100644 --- a/src/test/java/domain/LottoTest.java +++ b/src/test/java/domain/LottoTest.java @@ -1,5 +1,6 @@ package domain; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.DisplayName; @@ -22,6 +23,14 @@ void throwsExceptionWhenLottoNumbersContainDuplicates() { ); } + @Test + @DisplayName("로또 번호가 정확히 6개면 생성된다") + void createsLottoWithSixNumbers() { + List numbers = List.of(1, 2, 3, 4, 5, 6); + + assertDoesNotThrow(() -> new Lotto(numbers)); + } + @ParameterizedTest @MethodSource("invalidLottoNumbers") @DisplayName("로또 번호가 6개가 아니면 예외가 발생한다") diff --git a/src/test/java/domain/WinningLottoTest.java b/src/test/java/domain/WinningLottoTest.java index 715bbd160..9abf1c0e7 100644 --- a/src/test/java/domain/WinningLottoTest.java +++ b/src/test/java/domain/WinningLottoTest.java @@ -13,7 +13,7 @@ public class WinningLottoTest { void throwsExceptionWhenBonusNumberIsDuplicatedWithWinningNumbers() { List numbers = List.of(1, 2, 3, 4, 5, 6); LottoNumber bonusNumber = new LottoNumber(3); - WinningNumbers winningNumbers = new WinningNumbers(numbers); + Lotto winningNumbers = new Lotto(numbers); assertThrows( IllegalArgumentException.class, diff --git a/src/test/java/domain/WinningNumbersTest.java b/src/test/java/domain/WinningNumbersTest.java deleted file mode 100644 index 1615e9a10..000000000 --- a/src/test/java/domain/WinningNumbersTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package domain; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class WinningNumbersTest { - @Test - @DisplayName("당첨 번호가 정확히 6개면 생성된다") - void createsWinningNumbersWithSixNumbers() { - List numbers = List.of(1, 2, 3, 4, 5, 6); - - assertDoesNotThrow(() -> new WinningNumbers(numbers)); - } - - @Test - @DisplayName("당첨 번호의 개수가 6개가 아니면 예외가 발생한다") - void throwsExceptionWhenWinningNumberCountIsInvalid() { - List numbers = List.of(1, 2, 3, 4, 5); - - assertThrows( - IllegalArgumentException.class, - () -> new WinningNumbers(numbers) - ); - } - - @Test - @DisplayName("당첨 번호에 중복된 번호가 있으면 예외가 발생한다") - void throwsExceptionWhenWinningNumbersContainDuplicates() { - List numbers = List.of(1, 2, 3, 4, 5, 5); - - assertThrows( - IllegalArgumentException.class, - () -> new WinningNumbers(numbers) - ); - } -}