diff --git a/README.md b/README.md
index 32b16cd0..1fe627a9 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
- `Application` : 로또 실행을 담당하는 진입점 클래스
-## model
+## domain
- `Price` : 구매 가격 VO
@@ -13,21 +13,21 @@
- `LottoNumber` : 로또 번호 VO
- `Lotto` : 로또 객체
- `Lottos` : 여러 개의 Lotto 객체를 관리하는 일급 컬렉션
-- `LottoResult` : 로또 결과 VO
+- `LottoResult` : 로또 당첨 결과 객체
+- `LottoCount` : 로또 구매 개수 VO
#### constant
-- `LottoBoundary` : 로또 경계값 상수
-- `LottoMatch` : 로또 당첨 결과 상수
+- `WinningRank` : 당첨 순위 Enum
-## view
+## io
- `InputView` : 입력을 담당하는 클래스
- `OutputView` : 출력을 담당하는 클래스
-## controller
+## game
-- `LottoController` : 로또 전체 흐름을 제어하는 클래스
+- `LottoGame` : 로또 게임을 담당하는 클래스
@@ -44,23 +44,23 @@
```java
구입금액을 입력해 주세요.
-14000
-
-14개를 구매했습니다.
-[8,21,23,41,42,43]
-[3,5,11,16,32,38]
-[7,11,16,35,36,44]
-[1,8,11,31,41,42]
-[13,14,16,38,42,45]
-[7,11,30,40,42,43]
-[2,13,22,32,38,45]
-[23,25,33,36,39,41]
-[1,3,5,14,22,45]
-[5,9,38,41,43,44]
-[2,8,9,18,19,21]
-[13,14,18,21,23,35]
-[17,21,29,37,42,45]
-[3,8,27,30,35,44]
+ 14000
+
+ 14개를 구매했습니다.
+ [8,21,23,41,42,43]
+ [3,5,11,16,32,38]
+ [7,11,16,35,36,44]
+ [1,8,11,31,41,42]
+ [13,14,16,38,42,45]
+ [7,11,30,40,42,43]
+ [2,13,22,32,38,45]
+ [23,25,33,36,39,41]
+ [1,3,5,14,22,45]
+ [5,9,38,41,43,44]
+ [2,8,9,18,19,21]
+ [13,14,18,21,23,35]
+ [17,21,29,37,42,45]
+ [3,8,27,30,35,44]
```
### 새로운 프로그래밍 요구사항
@@ -90,34 +90,34 @@
```java
구입금액을 입력해 주세요.
-14000
-
-14개를 구매했습니다.
-[8,21,23,41,42,43]
-[3,5,11,16,32,38]
-[7,11,16,35,36,44]
-[1,8,11,31,41,42]
-[13,14,16,38,42,45]
-[7,11,30,40,42,43]
-[2,13,22,32,38,45]
-[23,25,33,36,39,41]
-[1,3,5,14,22,45]
-[5,9,38,41,43,44]
-[2,8,9,18,19,21]
-[13,14,18,21,23,35]
-[17,21,29,37,42,45]
-[3,8,27,30,35,44]
-
-지난 주 당첨 번호를 입력해 주세요.
-1,2,3,4,5,6
-
-당첨 통계
----------
-3개 일치(5000원)-1개
-4개 일치(50000원)-0개
-5개 일치(1500000원)-0개
-6개 일치(2000000000원)-0개
-총 수익률은0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
+ 14000
+
+ 14개를 구매했습니다.
+ [8,21,23,41,42,43]
+ [3,5,11,16,32,38]
+ [7,11,16,35,36,44]
+ [1,8,11,31,41,42]
+ [13,14,16,38,42,45]
+ [7,11,30,40,42,43]
+ [2,13,22,32,38,45]
+ [23,25,33,36,39,41]
+ [1,3,5,14,22,45]
+ [5,9,38,41,43,44]
+ [2,8,9,18,19,21]
+ [13,14,18,21,23,35]
+ [17,21,29,37,42,45]
+ [3,8,27,30,35,44]
+
+ 지난 주 당첨 번호를 입력해 주세요.
+ 1,2,3,4,5,6
+
+ 당첨 통계
+ ---------
+ 3개 일치(5000원)-1개
+ 4개 일치(50000원)-0개
+ 5개 일치(1500000원)-0개
+ 6개 일치(2000000000원)-0개
+ 총 수익률은0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
```
@@ -130,47 +130,106 @@
- 2등을 위한 보너스볼을 추첨한다.
- 당첨 통계에 2등을 추가한다.
- - 2등 당첨 조건은 당첨 번호 5개 일치 + 보너스 볼 일치다.
+ - 2등 당첨 조건은 당첨 번호 5개 일치 + 보너스 볼 일치다.
-실행 결과
+
+- 실행 결과
위 요구사항에 따라 14000원 어치 로또를 구매하였을 경우 프로그램을 실행한 결과는 다음과 같다.
```java
구입금액을 입력해 주세요.
-14000
-
-14개를 구매했습니다.
-[8,21,23,41,42,43]
-[3,5,11,16,32,38]
-[7,11,16,35,36,44]
-[1,8,11,31,41,42]
-[13,14,16,38,42,45]
-[7,11,30,40,42,43]
-[2,13,22,32,38,45]
-[23,25,33,36,39,41]
-[1,3,5,14,22,45]
-[5,9,38,41,43,44]
-[2,8,9,18,19,21]
-[13,14,18,21,23,35]
-[17,21,29,37,42,45]
-[3,8,27,30,35,44]
-
-지난 주 당첨 번호를 입력해 주세요.
-1,2,3,4,5,6
-
-보너스 볼을 입력해 주세요.
-7
-
-당첨 통계
----------
-3개 일치(5000원)-1개
-4개 일치(50000원)-0개
-5개 일치(1500000원)-0개
-5개 일치,보너스 볼 일치(30000000원)-0개
-6개 일치(2000000000원)-0개
-총 수익률은0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
+ 14000
+
+ 14개를 구매했습니다.
+ [8,21,23,41,42,43]
+ [3,5,11,16,32,38]
+ [7,11,16,35,36,44]
+ [1,8,11,31,41,42]
+ [13,14,16,38,42,45]
+ [7,11,30,40,42,43]
+ [2,13,22,32,38,45]
+ [23,25,33,36,39,41]
+ [1,3,5,14,22,45]
+ [5,9,38,41,43,44]
+ [2,8,9,18,19,21]
+ [13,14,18,21,23,35]
+ [17,21,29,37,42,45]
+ [3,8,27,30,35,44]
+
+ 지난 주 당첨 번호를 입력해 주세요.
+ 1,2,3,4,5,6
+
+ 보너스 볼을 입력해 주세요.
+ 7
+
+ 당첨 통계
+ ---------
+ 3개 일치(5000원)-1개
+ 4개 일치(50000원)-0개
+ 5개 일치(1500000원)-0개
+ 5개 일치,보너스 볼 일치(30000000원)-0개
+ 6개 일치(2000000000원)-0개
+ 총 수익률은0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
```
### 새로운 프로그래밍 요구사항
+
Java Enum을 적용한다.
+
+### 4단계 - 로또 수동 구매
+
+- 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다.
+- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다.
+
+실행 결과
+
+- 위 요구사항에 따라 14000원 어치 중 수동 3개, 자동 11개를 구매한 경우 프로그램을 실행한 결과는 다음과 같다.
+
+```java
+구입금액을 입력해 주세요.
+ 14000
+
+ 수동으로 구매할 로또 수를 입력해 주세요.
+ 3
+
+ 수동으로 구매할 번호를 입력해 주세요.
+ 8,21,23,41,42,43
+ 3,5,11,16,32,38
+ 7,11,16,35,36,44
+
+ 수동으로 3장,자동으로 11개를 구매했습니다.
+ [8,21,23,41,42,43]
+ [3,5,11,16,32,38]
+ [7,11,16,35,36,44]
+ [1,8,11,31,41,42]
+ [13,14,16,38,42,45]
+ [7,11,30,40,42,43]
+ [2,13,22,32,38,45]
+ [23,25,33,36,39,41]
+ [1,3,5,14,22,45]
+ [5,9,38,41,43,44]
+ [2,8,9,18,19,21]
+ [13,14,18,21,23,35]
+ [17,21,29,37,42,45]
+ [3,8,27,30,35,44]
+
+ 지난 주 당첨 번호를 입력해 주세요.
+ 1,2,3,4,5,6
+
+ 보너스 볼을 입력해 주세요.
+ 7
+
+ 당첨 통계
+ ---------
+ 3개 일치(5000원)-1개
+ 4개 일치(50000원)-0개
+ 5개 일치(1500000원)-0개
+ 5개 일치,보너스 볼 일치(30000000원)-0개
+ 6개 일치(2000000000원)-0개
+ 총 수익률은0.35입니다.(기준이 1이기 때문에 결과적으로 손해라는 의미임)
+```
+
+### 5단계 - 리팩터링
+
+- 기존 프로그래밍 요구사항을 다시 한번 확인하고, 학습 테스트를 통해 학습한 내용을 반영한다.
diff --git a/src/main/java/org/duckstudy/Application.java b/src/main/java/org/duckstudy/Application.java
index 811618ef..6fcc749d 100644
--- a/src/main/java/org/duckstudy/Application.java
+++ b/src/main/java/org/duckstudy/Application.java
@@ -2,17 +2,17 @@
import java.io.BufferedReader;
import java.io.InputStreamReader;
-import org.duckstudy.controller.LottoController;
-import org.duckstudy.view.InputView;
-import org.duckstudy.view.OutputView;
+import org.duckstudy.game.LottoGame;
+import org.duckstudy.io.InputView;
+import org.duckstudy.io.OutputView;
public class Application {
public static void main(String[] args) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
OutputView outputView = new OutputView();
- InputView inputView = new InputView(bufferedReader, outputView);
+ InputView inputView = new InputView(bufferedReader);
- LottoController lottoController = new LottoController(outputView, inputView);
- lottoController.run();
+ LottoGame lottoGame = new LottoGame(outputView, inputView);
+ lottoGame.run();
}
}
diff --git a/src/main/java/org/duckstudy/controller/LottoController.java b/src/main/java/org/duckstudy/controller/LottoController.java
deleted file mode 100644
index 6d570936..00000000
--- a/src/main/java/org/duckstudy/controller/LottoController.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.duckstudy.controller;
-
-import org.duckstudy.model.Price;
-import org.duckstudy.model.lotto.Lotto;
-import org.duckstudy.model.lotto.LottoNumber;
-import org.duckstudy.model.lotto.LottoResult;
-import org.duckstudy.model.lotto.Lottos;
-import org.duckstudy.view.InputView;
-import org.duckstudy.view.OutputView;
-
-public class LottoController {
-
- private final OutputView outputView;
- private final InputView inputView;
-
- public LottoController(OutputView outputView, InputView inputView) {
- this.outputView = outputView;
- this.inputView = inputView;
- }
-
- public void run() {
- Price price = createPrice();
- Lottos lottos = Lottos.generateLottosByPrice(price);
- outputView.printLottos(lottos);
-
- Lotto winningLotto = createWinningLotto();
- LottoNumber bonusNumber = createBonusNumber(winningLotto);
-
- getWinningResult(price, lottos, winningLotto, bonusNumber);
- }
-
- private Price createPrice() {
- try {
- Price price = new Price(inputView.inputPrice());
- price.validateInputPrice();
- return price;
- } catch (IllegalArgumentException e) {
- outputView.printException(e);
- return createPrice();
- }
- }
-
- private Lotto createWinningLotto() {
- try {
- return Lotto.from(inputView.inputWinningLotto());
- } catch (IllegalArgumentException e) {
- outputView.printException(e);
- return createWinningLotto();
- }
- }
-
- private LottoNumber createBonusNumber(Lotto winningLotto) {
- LottoNumber bonusNumber = LottoNumber.valueOf(inputView.inputBonusNumber());
- if (winningLotto.containsNumber(bonusNumber)) {
- outputView.printExceptionForBonusNumber();
- return createBonusNumber(winningLotto);
- }
- outputView.printExceptionForBonusNumber();
- return bonusNumber;
- }
-
- private void getWinningResult(Price price, Lottos lottos, Lotto winningLotto, LottoNumber bonusNumber) {
- LottoResult result = createLottoResult(lottos, winningLotto, bonusNumber);
-
- calculateProfitRate(price, result);
- }
-
- private LottoResult createLottoResult(Lottos lottos, Lotto winningLotto, LottoNumber bonusNumber) {
- LottoResult result = lottos.accumulateLottoResult(winningLotto, bonusNumber);
- outputView.printLottoResult(result);
- return result;
- }
-
- private void calculateProfitRate(Price price, LottoResult result) {
- double profitRate = price.calculateProfitRate(result);
- outputView.printTotalProfit(profitRate);
- }
-}
diff --git a/src/main/java/org/duckstudy/model/Price.java b/src/main/java/org/duckstudy/domain/Price.java
similarity index 64%
rename from src/main/java/org/duckstudy/model/Price.java
rename to src/main/java/org/duckstudy/domain/Price.java
index e1184485..f06c63d5 100644
--- a/src/main/java/org/duckstudy/model/Price.java
+++ b/src/main/java/org/duckstudy/domain/Price.java
@@ -1,8 +1,9 @@
-package org.duckstudy.model;
+package org.duckstudy.domain;
import java.util.Objects;
-import org.duckstudy.model.lotto.LottoResult;
-import org.duckstudy.model.lotto.constant.WinningRank;
+import org.duckstudy.domain.lotto.LottoCount;
+import org.duckstudy.domain.lotto.LottoResult;
+import org.duckstudy.domain.lotto.constant.WinningRank;
public class Price {
@@ -13,16 +14,16 @@ public class Price {
private final int value;
- public Price(int price) {
+ public Price(final int price) {
validatePrice(price);
this.value = price;
}
- public static Price initialize() {
+ public static Price zero() {
return new Price(INCLUSIVE_MIN_PRICE);
}
- private void validatePrice(int price) {
+ private void validatePrice(final int price) {
if (price < INCLUSIVE_MIN_PRICE) {
throw new IllegalArgumentException(String.format("가격은 %d원 이상이어야 합니다.", INCLUSIVE_MIN_PRICE));
}
@@ -34,41 +35,36 @@ public void validateInputPrice() {
}
}
- public Price addPrice(int value) {
+ public Price addPrice(final int value) {
return new Price(this.value + value);
}
- public Price multiplyTimes(int times) {
- return new Price(value * times);
- }
-
- public double divideByPrice(Price divisor) {
+ public double divideByPrice(final Price divisor) {
checkIfZero(divisor.getValue());
return (double) value / divisor.getValue();
}
- private void checkIfZero(int divisor) {
+ private void checkIfZero(final int divisor) {
if (divisor == ZERO) {
throw new IllegalArgumentException(String.format("%d으로 나눌 수 없습니다.", ZERO));
}
}
- public int calculateLottoCount() {
+ public LottoCount calculateLottoCount() {
checkIfZero(LOTTO_PRICE);
- return value / LOTTO_PRICE;
+ return new LottoCount(value / LOTTO_PRICE);
}
- public double calculateProfitRate(LottoResult result) {
- Price profit = Price.initialize();
+ public double calculateProfitRate(final LottoResult result) {
+ Price profit = Price.zero();
for (WinningRank winningRank : WinningRank.values()) {
- profit = profit.accumulateProfit(winningRank, result.getMatchingCount(winningRank.getKey()));
+ profit = profit.accumulateProfit(winningRank.getPrice(), result.getMatchingCount(winningRank));
}
return profit.divideByPrice(this) * PERCENT_BASE;
}
- private Price accumulateProfit(WinningRank winningRank, int count) {
- return this.addPrice(winningRank.getPrice())
- .multiplyTimes(count);
+ private Price accumulateProfit(final int price, final int count) {
+ return this.addPrice(price * count);
}
public int getValue() {
diff --git a/src/main/java/org/duckstudy/model/lotto/Lotto.java b/src/main/java/org/duckstudy/domain/lotto/Lotto.java
similarity index 53%
rename from src/main/java/org/duckstudy/model/lotto/Lotto.java
rename to src/main/java/org/duckstudy/domain/lotto/Lotto.java
index aeeef811..a8e43748 100644
--- a/src/main/java/org/duckstudy/model/lotto/Lotto.java
+++ b/src/main/java/org/duckstudy/domain/lotto/Lotto.java
@@ -1,40 +1,42 @@
-package org.duckstudy.model.lotto;
+package org.duckstudy.domain.lotto;
import static java.util.Collections.shuffle;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
-import static org.duckstudy.model.lotto.constant.LottoNumberRange.END_INCLUSIVE_NUMBER;
-import static org.duckstudy.model.lotto.constant.LottoNumberRange.START_INCLUSIVE_NUMBER;
+import static java.util.stream.Collectors.toSet;
+import static org.duckstudy.domain.lotto.LottoNumber.END_INCLUSIVE_NUMBER;
+import static org.duckstudy.domain.lotto.LottoNumber.START_INCLUSIVE_NUMBER;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Lotto {
- private static final List NUMBERS;
+ private static final Set NUMBERS;
private static final int LOTTO_SIZE = 6;
static {
- NUMBERS = IntStream.range(START_INCLUSIVE_NUMBER.getValue(), END_INCLUSIVE_NUMBER.getValue() + 1)
+ NUMBERS = IntStream.range(START_INCLUSIVE_NUMBER, END_INCLUSIVE_NUMBER + 1)
.boxed()
- .toList();
+ .collect(toSet());
}
- private final List lotto;
+ private final Set lotto;
- public Lotto(List lotto) {
+ public Lotto(final Set lotto) {
validateLottoSize(lotto);
- validateDuplicate(lotto);
- this.lotto = Collections.unmodifiableList(lotto);
+ this.lotto = new HashSet<>(lotto);
}
- public static Lotto from(List values) {
+ public static Lotto from(final Set values) {
return new Lotto(values.stream()
.map(LottoNumber::valueOf)
- .collect(toList()));
+ .collect(toSet()));
}
public static Lotto createRandomLotto() {
@@ -43,7 +45,7 @@ public static Lotto createRandomLotto() {
.limit(LOTTO_SIZE)
.sorted()
.map(LottoNumber::valueOf)
- .collect(toList()));
+ .collect(toSet()));
}
private static Collector> getCollector() {
@@ -54,30 +56,24 @@ public static Lotto createRandomLotto() {
});
}
- public int countMatchingNumber(Lotto compareLotto) {
+ public int countMatchingNumber(final Lotto compareLotto) {
return lotto.stream()
.filter(lottoNumber -> compareLotto.getLotto().contains(lottoNumber))
.toList()
.size();
}
- public boolean containsNumber(LottoNumber lottoNumber) {
+ public boolean containsNumber(final LottoNumber lottoNumber) {
return lotto.contains(lottoNumber);
}
- private void validateLottoSize(List lotto) {
+ private void validateLottoSize(final Set lotto) {
if (lotto.size() != LOTTO_SIZE) {
- throw new IllegalArgumentException(String.format("로또 번호는 %d개여야 합니다.", LOTTO_SIZE));
+ throw new IllegalArgumentException(String.format("로또 번호는 중복되지 않은 %d개여야 합니다.\n", LOTTO_SIZE));
}
}
- private void validateDuplicate(List lotto) {
- if (lotto.stream().distinct().count() != LOTTO_SIZE) {
- throw new IllegalArgumentException("로또 번호는 중복되지 않아야 합니다.");
- }
- }
-
- public List getLotto() {
- return lotto;
+ public Set getLotto() {
+ return Collections.unmodifiableSet(lotto);
}
}
diff --git a/src/main/java/org/duckstudy/domain/lotto/LottoCount.java b/src/main/java/org/duckstudy/domain/lotto/LottoCount.java
new file mode 100644
index 00000000..7bf975eb
--- /dev/null
+++ b/src/main/java/org/duckstudy/domain/lotto/LottoCount.java
@@ -0,0 +1,50 @@
+package org.duckstudy.domain.lotto;
+
+import java.util.Objects;
+
+public class LottoCount {
+
+ private final int count;
+
+ public LottoCount(final int count) {
+ validateLottoCount(count);
+ this.count = count;
+ }
+
+ private void validateLottoCount(final int count) {
+ if (count < 0) {
+ throw new IllegalArgumentException("로또 개수는 0개 이상이어야 합니다.");
+ }
+ }
+
+ public void validateManualLottoCount(final LottoCount totalLottoCount) {
+ if (count > totalLottoCount.getCount()) {
+ throw new IllegalArgumentException("수동으로 구매할 로또 수가 전체 로또 수를 초과합니다.\n");
+ }
+ }
+
+ public LottoCount subtract(final LottoCount lottoCount) {
+ return new LottoCount(count - lottoCount.count);
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ LottoCount that = (LottoCount) o;
+ return count == that.count;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(count);
+ }
+}
diff --git a/src/main/java/org/duckstudy/model/lotto/LottoNumber.java b/src/main/java/org/duckstudy/domain/lotto/LottoNumber.java
similarity index 55%
rename from src/main/java/org/duckstudy/model/lotto/LottoNumber.java
rename to src/main/java/org/duckstudy/domain/lotto/LottoNumber.java
index 62a18119..d51dac4f 100644
--- a/src/main/java/org/duckstudy/model/lotto/LottoNumber.java
+++ b/src/main/java/org/duckstudy/domain/lotto/LottoNumber.java
@@ -1,7 +1,4 @@
-package org.duckstudy.model.lotto;
-
-import static org.duckstudy.model.lotto.constant.LottoNumberRange.END_INCLUSIVE_NUMBER;
-import static org.duckstudy.model.lotto.constant.LottoNumberRange.START_INCLUSIVE_NUMBER;
+package org.duckstudy.domain.lotto;
import java.util.List;
import java.util.Objects;
@@ -9,31 +6,33 @@
public class LottoNumber {
+ public static final int START_INCLUSIVE_NUMBER = 1;
+ public static final int END_INCLUSIVE_NUMBER = 45;
private static final List cache;
static {
- cache = IntStream.range(0, END_INCLUSIVE_NUMBER.getValue())
- .mapToObj(i -> new LottoNumber(START_INCLUSIVE_NUMBER.getValue() + i))
+ cache = IntStream.range(0, END_INCLUSIVE_NUMBER)
+ .mapToObj(i -> new LottoNumber(START_INCLUSIVE_NUMBER + i))
.toList();
}
private final int value;
- private LottoNumber(int number) {
+ private LottoNumber(final int number) {
this.value = number;
}
- public static LottoNumber valueOf(int number) {
+ public static LottoNumber valueOf(final int number) {
validateNumber(number);
- return cache.get(number - START_INCLUSIVE_NUMBER.getValue());
+ return cache.get(number - START_INCLUSIVE_NUMBER);
}
- private static void validateNumber(int number) {
- if (number < START_INCLUSIVE_NUMBER.getValue() || number > END_INCLUSIVE_NUMBER.getValue()) {
+ private static void validateNumber(final int number) {
+ if (number < START_INCLUSIVE_NUMBER || number > END_INCLUSIVE_NUMBER) {
throw new IllegalArgumentException(
- String.format("로또 번호는 %d 이상 %d 이하의 숫자여야 합니다.", START_INCLUSIVE_NUMBER.getValue(),
- END_INCLUSIVE_NUMBER.getValue()));
+ String.format("로또 번호는 %d 이상 %d 이하의 숫자여야 합니다.", START_INCLUSIVE_NUMBER,
+ END_INCLUSIVE_NUMBER));
}
}
diff --git a/src/main/java/org/duckstudy/domain/lotto/LottoResult.java b/src/main/java/org/duckstudy/domain/lotto/LottoResult.java
new file mode 100644
index 00000000..a44d7c3e
--- /dev/null
+++ b/src/main/java/org/duckstudy/domain/lotto/LottoResult.java
@@ -0,0 +1,48 @@
+package org.duckstudy.domain.lotto;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.duckstudy.domain.lotto.constant.WinningRank;
+
+public class LottoResult {
+
+ public static final int DEFAULT_FREQUENCY = 1;
+ public static final int DEFAULT_VALUE = 0;
+
+ private final Map result;
+
+ public LottoResult(final Map result) {
+ this.result = new HashMap<>(result);
+ }
+
+ public static LottoResult createLottoResult(final Lotto lotto, final Lotto winningLotto,
+ final LottoNumber bonusNumber) {
+ int matchingCount = lotto.countMatchingNumber(winningLotto);
+ boolean matchBonus = lotto.containsNumber(bonusNumber);
+
+ WinningRank winningRank = WinningRank.findByMatchCountAndBonus(matchingCount, matchBonus);
+
+ return new LottoResult(Map.of(winningRank, DEFAULT_FREQUENCY));
+ }
+
+ public LottoResult merge(final LottoResult other) {
+ return new LottoResult(Stream.of(this.result, other.result)
+ .flatMap(map -> map.entrySet().stream())
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ Map.Entry::getValue,
+ Integer::sum
+ )));
+ }
+
+ public int getMatchingCount(final WinningRank winningRank) {
+ return result.getOrDefault(winningRank, DEFAULT_VALUE);
+ }
+
+ public Map getResult() {
+ return Collections.unmodifiableMap(result);
+ }
+}
diff --git a/src/main/java/org/duckstudy/model/lotto/Lottos.java b/src/main/java/org/duckstudy/domain/lotto/Lottos.java
similarity index 55%
rename from src/main/java/org/duckstudy/model/lotto/Lottos.java
rename to src/main/java/org/duckstudy/domain/lotto/Lottos.java
index 9c4690e3..9a40e2b9 100644
--- a/src/main/java/org/duckstudy/model/lotto/Lottos.java
+++ b/src/main/java/org/duckstudy/domain/lotto/Lottos.java
@@ -1,41 +1,41 @@
-package org.duckstudy.model.lotto;
+package org.duckstudy.domain.lotto;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
-import org.duckstudy.model.Price;
public class Lottos {
private final List lottos;
- public Lottos(List lottos) {
- this.lottos = Collections.unmodifiableList(lottos);
+ public Lottos(final List lottos) {
+ this.lottos = new ArrayList<>(lottos);
}
- public static Lottos generateLottosByPrice(Price price) {
- int lottoCount = price.calculateLottoCount();
-
+ public static Lottos generateLottos(final int lottoCount) {
return Stream.generate(Lotto::createRandomLotto)
.limit(lottoCount)
.collect(collectingAndThen(toList(), Lottos::new));
}
- public LottoResult accumulateLottoResult(Lotto winningLotto, LottoNumber bonusNumber) {
+ public LottoResult accumulateLottoResult(final Lotto winningLotto, final LottoNumber bonusNumber) {
return lottos.stream()
.map(lotto -> LottoResult.createLottoResult(lotto, winningLotto, bonusNumber))
.reduce(new LottoResult(Map.of()), LottoResult::merge);
}
- public List getLottos() {
- return lottos;
+ public Lottos merge(final Lottos other) {
+ return new Lottos(Stream.of(this.lottos, other.lottos)
+ .flatMap(List::stream)
+ .collect(toList()));
}
- public int getSize() {
- return lottos.size();
+ public List getLottos() {
+ return Collections.unmodifiableList(lottos);
}
}
diff --git a/src/main/java/org/duckstudy/domain/lotto/constant/WinningRank.java b/src/main/java/org/duckstudy/domain/lotto/constant/WinningRank.java
new file mode 100644
index 00000000..0abbac9c
--- /dev/null
+++ b/src/main/java/org/duckstudy/domain/lotto/constant/WinningRank.java
@@ -0,0 +1,45 @@
+package org.duckstudy.domain.lotto.constant;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.BiPredicate;
+
+public enum WinningRank {
+
+ NONE(List.of(0, 1, 2), 0, (matchCount, matchBonus) -> matchCount < 3),
+ FIFTH(List.of(3), 5_000, (matchCount, matchBonus) -> matchCount == 3),
+ FOURTH(List.of(4), 50_000, (matchCount, matchBonus) -> matchCount == 4),
+ THIRD(List.of(5), 1_500_000, (matchCount, matchBonus) -> matchCount == 5 && !matchBonus),
+ SECOND(List.of(5), 30_000_000, (matchCount, matchBonus) -> matchCount == 5 && matchBonus),
+ FIRST(List.of(6), 2_000_000_000, (matchCount, matchBonus) -> matchCount == 6);
+
+ private final List matchCount;
+ private final int price;
+ private final BiPredicate isMatchPredicate;
+
+ WinningRank(final List matchCount, final int price,
+ final BiPredicate isMatchPredicate) {
+ this.matchCount = matchCount;
+ this.price = price;
+ this.isMatchPredicate = isMatchPredicate;
+ }
+
+ public static WinningRank findByMatchCountAndBonus(final int matchCount, final boolean matchBonus) {
+ return Arrays.stream(values())
+ .filter(winningLank -> winningLank.isMatch(matchCount, matchBonus))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("적절한 당첨 등수를 찾을 수 없습니다."));
+ }
+
+ private boolean isMatch(final int matchCount, final boolean matchBonus) {
+ return isMatchPredicate.test(matchCount, matchBonus);
+ }
+
+ public List getMatchCount() {
+ return matchCount;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+}
diff --git a/src/main/java/org/duckstudy/game/LottoGame.java b/src/main/java/org/duckstudy/game/LottoGame.java
new file mode 100644
index 00000000..b06cd180
--- /dev/null
+++ b/src/main/java/org/duckstudy/game/LottoGame.java
@@ -0,0 +1,123 @@
+package org.duckstudy.game;
+
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.duckstudy.domain.Price;
+import org.duckstudy.domain.lotto.Lotto;
+import org.duckstudy.domain.lotto.LottoCount;
+import org.duckstudy.domain.lotto.LottoNumber;
+import org.duckstudy.domain.lotto.LottoResult;
+import org.duckstudy.domain.lotto.Lottos;
+import org.duckstudy.io.InputView;
+import org.duckstudy.io.OutputView;
+
+public class LottoGame {
+
+ private final OutputView outputView;
+ private final InputView inputView;
+
+ public LottoGame(final OutputView outputView, final InputView inputView) {
+ this.outputView = outputView;
+ this.inputView = inputView;
+ }
+
+ public void run() {
+ Price price = createPrice();
+ Lottos totalLottos = createTotalLottos(price);
+
+ Lotto winningLotto = createWinningLotto();
+ LottoNumber bonusNumber = createBonusNumber(winningLotto);
+
+ printWinningResult(price, totalLottos, winningLotto, bonusNumber);
+ }
+
+ private Lottos createTotalLottos(final Price price) {
+ LottoCount totalLottoCount = price.calculateLottoCount();
+ LottoCount manualLottoCount = createManualLottoCount(totalLottoCount);
+ Lottos manualLottos = createManualLottos(manualLottoCount.getCount());
+
+ LottoCount autoLottoCount = totalLottoCount.subtract(manualLottoCount);
+ Lottos autoLottos = Lottos.generateLottos(autoLottoCount.getCount());
+ Lottos totalLottos = manualLottos.merge(autoLottos);
+
+ outputView.printLottos(manualLottoCount.getCount(), autoLottoCount.getCount(), totalLottos);
+ return totalLottos;
+ }
+
+ private Price createPrice() {
+ outputView.printInputPrice();
+ try {
+ Price price = new Price(inputView.inputPrice());
+ price.validateInputPrice();
+ return price;
+ } catch (IllegalArgumentException e) {
+ outputView.printException(e);
+ return createPrice();
+ }
+ }
+
+ private Lottos createManualLottos(final int manualLottoCount) {
+ outputView.printInputManualLotto();
+
+ return new Lottos(IntStream.range(0, manualLottoCount)
+ .mapToObj(i -> createManualLotto())
+ .collect(Collectors.toList()));
+ }
+
+ private LottoCount createManualLottoCount(final LottoCount lottoCount) {
+ outputView.printInputManualLottoCount();
+ try {
+ LottoCount manualLottoCount = new LottoCount(inputView.inputManualLottoCount());
+ manualLottoCount.validateManualLottoCount(lottoCount);
+ return manualLottoCount;
+ } catch (IllegalArgumentException e) {
+ outputView.printException(e);
+ return createManualLottoCount(lottoCount);
+ }
+ }
+
+ private Lotto createManualLotto() {
+ try {
+ return Lotto.from(inputView.inputManualLotto());
+ } catch (IllegalArgumentException e) {
+ outputView.printException(e);
+ return createManualLotto();
+ }
+ }
+
+ private Lotto createWinningLotto() {
+ outputView.printInputWinningLotto();
+ try {
+ return Lotto.from(inputView.inputWinningLotto());
+ } catch (IllegalArgumentException e) {
+ outputView.printException(e);
+ return createWinningLotto();
+ }
+ }
+
+ private LottoNumber createBonusNumber(final Lotto winningLotto) {
+ outputView.printInputBonusNumber();
+ try {
+ LottoNumber bonusNumber = LottoNumber.valueOf(inputView.inputBonusNumber());
+ validateBonusNumber(winningLotto, bonusNumber);
+ return bonusNumber;
+ } catch (IllegalArgumentException e) {
+ outputView.printException(e);
+ return createBonusNumber(winningLotto);
+ }
+ }
+
+ private void validateBonusNumber(final Lotto winningLotto, final LottoNumber bonusNumber) {
+ if (winningLotto.containsNumber(bonusNumber)) {
+ throw new IllegalArgumentException("당첨 번호와 중복되는 보너스 볼은 입력할 수 없습니다.");
+ }
+ }
+
+ private void printWinningResult(Price price, Lottos totalLottos, Lotto winningLotto, LottoNumber bonusNumber) {
+ LottoResult lottoResult = totalLottos.accumulateLottoResult(winningLotto, bonusNumber);
+ outputView.printLottoResult(lottoResult);
+
+ double profitRate = price.calculateProfitRate(lottoResult);
+ outputView.printTotalProfit(profitRate);
+ }
+}
diff --git a/src/main/java/org/duckstudy/view/InputView.java b/src/main/java/org/duckstudy/io/InputView.java
similarity index 57%
rename from src/main/java/org/duckstudy/view/InputView.java
rename to src/main/java/org/duckstudy/io/InputView.java
index faf356c7..fec048bd 100644
--- a/src/main/java/org/duckstudy/view/InputView.java
+++ b/src/main/java/org/duckstudy/io/InputView.java
@@ -1,22 +1,21 @@
-package org.duckstudy.view;
+package org.duckstudy.io;
+
+import static java.util.stream.Collectors.toSet;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
public class InputView {
private final BufferedReader bufferedReader;
- private final OutputView outputView;
- public InputView(BufferedReader bufferedReader, OutputView outputView) {
+ public InputView(BufferedReader bufferedReader) {
this.bufferedReader = bufferedReader;
- this.outputView = outputView;
}
public int inputPrice() {
- outputView.printInputPrice();
try {
return Integer.parseInt(bufferedReader.readLine());
} catch (NumberFormatException | IOException e) {
@@ -24,9 +23,7 @@ public int inputPrice() {
}
}
- public List inputWinningLotto() {
- outputView.printInputWinningLotto();
-
+ public Set inputWinningLotto() {
try {
return inputLottoNumber();
} catch (NumberFormatException | IOException e) {
@@ -34,20 +31,34 @@ public List inputWinningLotto() {
}
}
- private List inputLottoNumber() throws IOException {
+ private Set inputLottoNumber() throws IOException {
return Arrays.stream(bufferedReader.readLine().split(","))
.map(String::trim)
.map(Integer::parseInt)
- .toList();
+ .collect(toSet());
}
public int inputBonusNumber() {
- outputView.printInputBonusNumber();
+ try {
+ return Integer.parseInt(bufferedReader.readLine());
+ } catch (NumberFormatException | IOException e) {
+ throw new NumberFormatException("숫자만 입력 가능합니다.");
+ }
+ }
+ public int inputManualLottoCount() {
try {
return Integer.parseInt(bufferedReader.readLine());
} catch (NumberFormatException | IOException e) {
throw new NumberFormatException("숫자만 입력 가능합니다.");
}
}
+
+ public Set inputManualLotto() {
+ try {
+ return inputLottoNumber();
+ } catch (NumberFormatException | IOException e) {
+ throw new NumberFormatException("숫자만 입력 가능합니다.\n");
+ }
+ }
}
diff --git a/src/main/java/org/duckstudy/io/OutputView.java b/src/main/java/org/duckstudy/io/OutputView.java
new file mode 100644
index 00000000..7beca5ab
--- /dev/null
+++ b/src/main/java/org/duckstudy/io/OutputView.java
@@ -0,0 +1,92 @@
+package org.duckstudy.io;
+
+import static org.duckstudy.domain.lotto.constant.WinningRank.NONE;
+import static org.duckstudy.domain.lotto.constant.WinningRank.SECOND;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import org.duckstudy.domain.lotto.Lotto;
+import org.duckstudy.domain.lotto.LottoNumber;
+import org.duckstudy.domain.lotto.LottoResult;
+import org.duckstudy.domain.lotto.Lottos;
+import org.duckstudy.domain.lotto.constant.WinningRank;
+
+public class OutputView {
+
+ public void printInputPrice() {
+ System.out.println("구입금액을 입력해 주세요.");
+ }
+
+ public void printException(final Exception e) {
+ System.out.println(e.getMessage());
+ }
+
+ public void printLottos(final int manualLottoCount, final int autoLottoCount, final Lottos lottos) {
+ System.out.printf("\n수동으로 %d개, 자동으로 %d개를 구매했습니다.\n", manualLottoCount, autoLottoCount);
+ lottos.getLottos()
+ .forEach(this::printLotto);
+ }
+
+ private void printLotto(final Lotto lotto) {
+ System.out.println(lotto.getLotto()
+ .stream()
+ .map(LottoNumber::getValue)
+ .sorted()
+ .map(String::valueOf)
+ .collect(Collectors.joining(", ", "[", "]")));
+ }
+
+ public void printInputWinningLotto() {
+ System.out.println("\n지난 주 당첨 번호를 입력해 주세요.");
+ }
+
+ public void printInputBonusNumber() {
+ System.out.println("\n보너스 볼을 입력해 주세요.");
+ }
+
+ public void printLottoResult(final LottoResult result) {
+ System.out.println("\n당첨 통계");
+ System.out.println("---------");
+ iterateLottoResult(result);
+ }
+
+ private void iterateLottoResult(final LottoResult result) {
+ for (WinningRank winningRank : WinningRank.values()) {
+ printMatchingResult(result, winningRank);
+ }
+ System.out.println();
+ }
+
+ private void printMatchingResult(final LottoResult result, final WinningRank winningRank) {
+ if (winningRank == NONE) {
+ return;
+ }
+
+ List matchCounts = winningRank.getMatchCount();
+ int price = winningRank.getPrice();
+
+ String matchPriceMessage = getMatchPrice(winningRank, matchCounts.get(0), price);
+ int matchingCount = result.getMatchingCount(winningRank);
+
+ System.out.println(matchPriceMessage + matchingCount + "개");
+ }
+
+ private String getMatchPrice(final WinningRank winningRank, final int cnt, final int price) {
+ if (winningRank == SECOND) {
+ return String.format("%d개 일치, 보너스 볼 일치(%d원)- ", cnt, price);
+ }
+ return String.format("%d개 일치 (%d원)- ", cnt, price);
+ }
+
+ public void printTotalProfit(final double totalProfitRate) {
+ System.out.printf("총 수익률은 %.2f입니다.\n", totalProfitRate);
+ }
+
+ public void printInputManualLottoCount() {
+ System.out.println("\n수동으로 구매할 로또 수를 입력해 주세요.");
+ }
+
+ public void printInputManualLotto() {
+ System.out.println("\n수동으로 구매할 번호를 입력해 주세요.");
+ }
+}
diff --git a/src/main/java/org/duckstudy/model/lotto/LottoResult.java b/src/main/java/org/duckstudy/model/lotto/LottoResult.java
deleted file mode 100644
index ec43bcc3..00000000
--- a/src/main/java/org/duckstudy/model/lotto/LottoResult.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.duckstudy.model.lotto;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.duckstudy.model.lotto.constant.WinningRank;
-
-public class LottoResult {
-
- public static final int DEFAULT_FREQUENCY = 1;
- public static final int DEFAULT_VALUE = 0;
-
- private final Map result;
-
- public LottoResult(Map result) {
- this.result = Collections.unmodifiableMap(result);
- }
-
- public static LottoResult createLottoResult(Lotto lotto, Lotto winningLotto, LottoNumber bonusNumber) {
- int matchingCount = lotto.countMatchingNumber(winningLotto);
- boolean matchBonus = lotto.containsNumber(bonusNumber);
-
- int key = WinningRank.findByMatchCountAndBonus(matchingCount, matchBonus)
- .getKey();
-
- return new LottoResult(Map.of(key, DEFAULT_FREQUENCY));
- }
-
- public LottoResult merge(LottoResult other) {
- return new LottoResult(Stream.of(this.result, other.result)
- .flatMap(map -> map.entrySet().stream())
- .collect(Collectors.toMap(
- Map.Entry::getKey,
- Map.Entry::getValue,
- Integer::sum
- )));
- }
-
- public int getMatchingCount(int count) {
- return result.getOrDefault(count, DEFAULT_VALUE);
- }
-
- public Map getResult() {
- return result;
- }
-}
diff --git a/src/main/java/org/duckstudy/model/lotto/constant/LottoNumberRange.java b/src/main/java/org/duckstudy/model/lotto/constant/LottoNumberRange.java
deleted file mode 100644
index f499c792..00000000
--- a/src/main/java/org/duckstudy/model/lotto/constant/LottoNumberRange.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.duckstudy.model.lotto.constant;
-
-public enum LottoNumberRange {
-
- START_INCLUSIVE_NUMBER(1),
- END_INCLUSIVE_NUMBER(45);
-
- private final int value;
-
- LottoNumberRange(int value) {
- this.value = value;
- }
-
- public int getValue() {
- return value;
- }
-}
diff --git a/src/main/java/org/duckstudy/model/lotto/constant/WinningRank.java b/src/main/java/org/duckstudy/model/lotto/constant/WinningRank.java
deleted file mode 100644
index 277b72ce..00000000
--- a/src/main/java/org/duckstudy/model/lotto/constant/WinningRank.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package org.duckstudy.model.lotto.constant;
-
-import java.util.stream.Stream;
-
-public enum WinningRank {
-
- NONE(0, 0, 0),
- FIFTH(3, 5000, 3),
- FOURTH(4, 50000, 4),
- THIRD(5, 1500000, 5),
- SECOND(5, 30000000, -5),
- FIRST(6, 2000000000, 6);
-
- private final int matchCount;
- private final int price;
- private final int key;
-
- WinningRank(int matchCount, int price, int key) {
- this.matchCount = matchCount;
- this.price = price;
- this.key = key;
- }
-
- public static WinningRank findByMatchCountAndBonus(int matchCount, boolean matchBonus) {
- if (matchCount == SECOND.getMatchCount() && matchBonus) {
- return SECOND;
- }
- return Stream.of(values())
- .filter(winningLank -> winningLank.getMatchCount() == matchCount)
- .findFirst()
- .orElse(NONE);
- }
-
- public int getMatchCount() {
- return matchCount;
- }
-
- public int getPrice() {
- return price;
- }
-
- public int getKey() {
- return key;
- }
-}
diff --git a/src/main/java/org/duckstudy/view/OutputView.java b/src/main/java/org/duckstudy/view/OutputView.java
deleted file mode 100644
index 460b6960..00000000
--- a/src/main/java/org/duckstudy/view/OutputView.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package org.duckstudy.view;
-
-import static org.duckstudy.model.lotto.constant.WinningRank.NONE;
-import static org.duckstudy.model.lotto.constant.WinningRank.SECOND;
-
-import java.util.stream.Collectors;
-import org.duckstudy.model.lotto.Lotto;
-import org.duckstudy.model.lotto.LottoNumber;
-import org.duckstudy.model.lotto.LottoResult;
-import org.duckstudy.model.lotto.Lottos;
-import org.duckstudy.model.lotto.constant.WinningRank;
-
-public class OutputView {
-
- public void printInputPrice() {
- System.out.println("구입금액을 입력해 주세요.");
- }
-
- public void printException(Exception e) {
- System.out.println(e.getMessage());
- }
-
- public void printLottos(Lottos lottos) {
- System.out.printf("\n%d개를 구매했습니다.\n", lottos.getSize());
- lottos.getLottos()
- .forEach(this::printLotto);
- }
-
- private void printLotto(Lotto lotto) {
- System.out.println(lotto.getLotto()
- .stream()
- .map(LottoNumber::getValue)
- .map(String::valueOf)
- .collect(Collectors.joining(", ", "[", "]")));
- }
-
- public void printInputWinningLotto() {
- System.out.println("\n지난 주 당첨 번호를 입력해 주세요.");
- }
-
- public void printInputBonusNumber() {
- System.out.println("\n보너스 볼을 입력해 주세요.");
- }
-
- public void printLottoResult(LottoResult result) {
- System.out.println("\n당첨 통계");
- System.out.println("---------");
- iterateLottoResult(result);
- }
-
- private void iterateLottoResult(LottoResult result) {
- for (WinningRank winningRank : WinningRank.values()) {
- printMatchingResult(result, winningRank);
- }
- System.out.println();
- }
-
- private void printMatchingResult(LottoResult result, WinningRank winningRank) {
- if (winningRank == NONE) {
- return;
- }
-
- int cnt = winningRank.getMatchCount();
- int key = winningRank.getKey();
- int price = winningRank.getPrice();
-
- String matchPriceMessage = getMatchPrice(winningRank, cnt, price);
- int matchingCount = result.getMatchingCount(key);
-
- System.out.println(matchPriceMessage + matchingCount + "개");
- }
-
- private String getMatchPrice(WinningRank winningRank, int cnt, int price) {
- if (winningRank == SECOND) {
- return String.format("%d개 일치, 보너스 볼 일치(%d원)- ", cnt, price);
- }
- return String.format("%d개 일치 (%d원)- ", cnt, price);
- }
-
- public void printTotalProfit(double totalProfitRate) {
- System.out.printf("총 수익률은 %.2f입니다.\n", totalProfitRate);
- }
-
- public void printExceptionForBonusNumber() {
- System.out.println("보너스 볼은 당첨 번호와 중복되면 안됩니다.");
- }
-}
diff --git a/src/test/java/org/duckstudy/model/PriceTest.java b/src/test/java/org/duckstudy/domain/PriceTest.java
similarity index 65%
rename from src/test/java/org/duckstudy/model/PriceTest.java
rename to src/test/java/org/duckstudy/domain/PriceTest.java
index df9875ab..7fb21d70 100644
--- a/src/test/java/org/duckstudy/model/PriceTest.java
+++ b/src/test/java/org/duckstudy/domain/PriceTest.java
@@ -1,4 +1,4 @@
-package org.duckstudy.model;
+package org.duckstudy.domain;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode;
@@ -6,8 +6,8 @@
import java.util.HashMap;
import java.util.Map;
-import org.duckstudy.model.lotto.LottoResult;
-import org.duckstudy.model.lotto.constant.WinningRank;
+import org.duckstudy.domain.lotto.LottoResult;
+import org.duckstudy.domain.lotto.constant.WinningRank;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -38,27 +38,57 @@ void validateFailWhenPriceIsLessThanZero() {
}
@Nested
- @DisplayName("가격 계산 테스트")
- class PriceCalculateTest {
+ @DisplayName("입력 가격 검증 테스트")
+ class InputPriceValidationTest {
@Test
- @DisplayName("가격을 더한다")
- void addPrice() {
+ @DisplayName("입력 가격이 양수일 경우 성공한다")
+ void validateInputPriceSuccessWhenPriceIsEqualOrGreaterThanZero() {
- Price price = new Price(1000);
+ Price price = new Price(1);
- Price result = price.addPrice(2000);
+ assertThatCode(price::validateInputPrice)
+ .doesNotThrowAnyException();
+ }
- assertThat(result).isEqualTo(new Price(3000));
+ @Test
+ @DisplayName("입력 가격이 0 이하일 경우 예외가 발생한다")
+ void validateInputPriceFailWhenPriceIsZero() {
+
+ Price price = new Price(0);
+
+ assertThatThrownBy(price::validateInputPrice)
+ .isExactlyInstanceOf(IllegalArgumentException.class)
+ .hasMessage("가격은 0원 이상이어야 합니다.\n");
+ }
+ }
+
+ @Nested
+ @DisplayName("가격 생성 테스트")
+ class PriceCreationTest {
+
+ @Test
+ @DisplayName("0원으로 가격을 생성하면 성공한다")
+ void createPriceWhenZero() {
+
+ Price price = Price.zero();
+
+ assertThat(price).isEqualTo(new Price(0));
}
+ }
+
+
+ @Nested
+ @DisplayName("가격 계산 테스트")
+ class PriceCalculateTest {
@Test
- @DisplayName("가격을 곱한다")
- void multiplyPrice() {
+ @DisplayName("가격을 더한다")
+ void addPrice() {
Price price = new Price(1000);
- Price result = price.multiplyTimes(3);
+ Price result = price.addPrice(2000);
assertThat(result).isEqualTo(new Price(3000));
}
@@ -96,18 +126,18 @@ void calculateLottoCountWhenInputPrice() {
Price price = new Price(10000);
- assertThat(price.calculateLottoCount()).isEqualTo(10);
+ assertThat(price.calculateLottoCount().getCount()).isEqualTo(10);
}
@Test
@DisplayName("로또 수익률을 계산한다")
void calculateProfitRate() {
Price purchasePrice = new Price(15000);
- Map result = new HashMap<>();
- result.put(WinningRank.FIRST.getKey(), 1);
+ Map result = new HashMap<>();
+ result.put(WinningRank.SECOND, 1);
LottoResult lottoResult = new LottoResult(result);
- assertThat(purchasePrice.calculateProfitRate(lottoResult)).isEqualTo(1.3333333333333334E7);
+ assertThat(purchasePrice.calculateProfitRate(lottoResult)).isEqualTo(200000.0);
}
}
}
diff --git a/src/test/java/org/duckstudy/domain/lotto/LottoCountTest.java b/src/test/java/org/duckstudy/domain/lotto/LottoCountTest.java
new file mode 100644
index 00000000..535bdde3
--- /dev/null
+++ b/src/test/java/org/duckstudy/domain/lotto/LottoCountTest.java
@@ -0,0 +1,79 @@
+package org.duckstudy.domain.lotto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+@DisplayName("로또 개수 테스트")
+class LottoCountTest {
+
+ @Nested
+ @DisplayName("로또 개수 생성 테스트")
+ class LottoCountCreationTest {
+
+ @Test
+ @DisplayName("로또 개수를 입력하면 로또 개수를 생성한다")
+ public void createLottoCountWhenInputCount() {
+
+ assertThatCode(() -> new LottoCount(10))
+ .doesNotThrowAnyException();
+ }
+
+ @Test
+ @DisplayName("로또 개수가 음수일 경우 예외가 발생한다")
+ public void createLottoCountFailWhenCountIsLessThanZero() {
+
+ assertThatThrownBy(() -> new LottoCount(-1))
+ .isExactlyInstanceOf(IllegalArgumentException.class)
+ .hasMessage("로또 개수는 0개 이상이어야 합니다.");
+ }
+ }
+
+ @Nested
+ @DisplayName("수동 로또 개수 검증 테스트")
+ class ManualLottoCountValidationTest {
+
+ @Test
+ @DisplayName("수동으로 구매할 로또 수가 전체 로또 수를 초과하지 않으면 성공한다")
+ public void validateManualLottoCountSuccessWhenManualLottoCountIsNotGreaterThanTotalLottoCount() {
+
+ LottoCount manualLottoCount = new LottoCount(10);
+ LottoCount totalLottoCount = new LottoCount(15);
+
+ assertThatCode(() -> manualLottoCount.validateManualLottoCount(totalLottoCount))
+ .doesNotThrowAnyException();
+ }
+
+ @Test
+ @DisplayName("수동으로 구매할 로또 수가 전체 로또 수를 초과하면 예외가 발생한다")
+ public void validateManualLottoCountFailWhenManualLottoCountIsGreaterThanTotalLottoCount() {
+
+ LottoCount manualLottoCount = new LottoCount(20);
+ LottoCount totalLottoCount = new LottoCount(15);
+
+ assertThatThrownBy(() -> manualLottoCount.validateManualLottoCount(totalLottoCount))
+ .isExactlyInstanceOf(IllegalArgumentException.class)
+ .hasMessage("수동으로 구매할 로또 수가 전체 로또 수를 초과합니다.\n");
+ }
+ }
+
+ @Nested
+ @DisplayName("로또 개수 계산 테스트")
+ class LottoCountCalculateTest {
+
+ @Test
+ @DisplayName("로또 개수를 뺀다")
+ public void subtractLottoCount() {
+
+ LottoCount lottoCount = new LottoCount(10);
+
+ LottoCount result = lottoCount.subtract(new LottoCount(5));
+
+ assertThat(result).isEqualTo(new LottoCount(5));
+ }
+ }
+}
diff --git a/src/test/java/org/duckstudy/model/lotto/LottoNumberTest.java b/src/test/java/org/duckstudy/domain/lotto/LottoNumberTest.java
similarity index 97%
rename from src/test/java/org/duckstudy/model/lotto/LottoNumberTest.java
rename to src/test/java/org/duckstudy/domain/lotto/LottoNumberTest.java
index 88376eb6..311e24fd 100644
--- a/src/test/java/org/duckstudy/model/lotto/LottoNumberTest.java
+++ b/src/test/java/org/duckstudy/domain/lotto/LottoNumberTest.java
@@ -1,4 +1,4 @@
-package org.duckstudy.model.lotto;
+package org.duckstudy.domain.lotto;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
diff --git a/src/test/java/org/duckstudy/model/lotto/LottoResultTest.java b/src/test/java/org/duckstudy/domain/lotto/LottoResultTest.java
similarity index 51%
rename from src/test/java/org/duckstudy/model/lotto/LottoResultTest.java
rename to src/test/java/org/duckstudy/domain/lotto/LottoResultTest.java
index 12624b4b..1be0dbcb 100644
--- a/src/test/java/org/duckstudy/model/lotto/LottoResultTest.java
+++ b/src/test/java/org/duckstudy/domain/lotto/LottoResultTest.java
@@ -1,10 +1,10 @@
-package org.duckstudy.model.lotto;
+package org.duckstudy.domain.lotto;
import static org.assertj.core.api.Assertions.assertThat;
-import java.util.List;
import java.util.Map;
-import org.duckstudy.model.lotto.constant.WinningRank;
+import java.util.Set;
+import org.duckstudy.domain.lotto.constant.WinningRank;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -12,29 +12,29 @@
class LottoResultTest {
@Test
- @DisplayName("로또 결과를 생성한다")
+ @DisplayName("로또 당첨 결과를 생성한다")
void createLottoResult() {
- Lotto lotto = Lotto.from(List.of(1, 2, 3, 4, 5, 7));
- Lotto winningLotto = Lotto.from(List.of(1, 2, 3, 4, 5, 6));
+ Lotto lotto = Lotto.from(Set.of(1, 2, 3, 4, 5, 7));
+ Lotto winningLotto = Lotto.from(Set.of(1, 2, 3, 4, 5, 6));
LottoNumber bonusNumber = LottoNumber.valueOf(7);
LottoResult lottoResult = LottoResult.createLottoResult(lotto, winningLotto, bonusNumber);
assertThat(lottoResult.getResult())
- .containsEntry(WinningRank.SECOND.getKey(), 1);
+ .containsEntry(WinningRank.SECOND, 1);
}
@Test
- @DisplayName("로또 결과를 병합한다")
+ @DisplayName("로또 당첨 결과를 병합한다")
void mergeLottoResult() {
- LottoResult lottoResult1 = new LottoResult(Map.of(3, 2));
- LottoResult lottoResult2 = new LottoResult(Map.of(3, 1));
+ LottoResult lottoResult1 = new LottoResult(Map.of(WinningRank.FIFTH, 2));
+ LottoResult lottoResult2 = new LottoResult(Map.of(WinningRank.FIFTH, 1));
LottoResult mergedLottoResult = lottoResult1.merge(lottoResult2);
assertThat(mergedLottoResult.getResult())
- .containsEntry(3, 3);
+ .containsEntry(WinningRank.FIFTH, 3);
}
}
diff --git a/src/test/java/org/duckstudy/model/lotto/LottoTest.java b/src/test/java/org/duckstudy/domain/lotto/LottoTest.java
similarity index 71%
rename from src/test/java/org/duckstudy/model/lotto/LottoTest.java
rename to src/test/java/org/duckstudy/domain/lotto/LottoTest.java
index ee561748..9c5c69ef 100644
--- a/src/test/java/org/duckstudy/model/lotto/LottoTest.java
+++ b/src/test/java/org/duckstudy/domain/lotto/LottoTest.java
@@ -1,10 +1,11 @@
-package org.duckstudy.model.lotto;
+package org.duckstudy.domain.lotto;
+import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import java.util.List;
+import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
@@ -25,9 +26,9 @@ class LottoCreationFromLottoNumberTest {
@DisplayName("로또를 생성한다")
void createLottoSuccess() {
- List lottoNumbers = Stream.of(1, 2, 3, 4, 5, 6)
+ Set lottoNumbers = Stream.of(1, 2, 3, 4, 5, 6)
.map(LottoNumber::valueOf)
- .toList();
+ .collect(toSet());
assertThatCode(() -> new Lotto(lottoNumbers))
.doesNotThrowAnyException();
@@ -37,26 +38,26 @@ void createLottoSuccess() {
@DisplayName("로또를 생성할 때 6개의 로또 번호가 아니면 예외를 발생한다")
void createLottoFailWhenSizeIsNotSix() {
- List lottoNumbers = List.of(
+ Set lottoNumbers = Set.of(
LottoNumber.valueOf(1)
);
assertThatThrownBy(() -> new Lotto(lottoNumbers))
.isInstanceOf(IllegalArgumentException.class)
- .hasMessage("로또 번호는 6개여야 합니다.");
+ .hasMessage("로또 번호는 중복되지 않은 6개여야 합니다.\n");
}
@Test
@DisplayName("로또를 생성할 때 중복된 로또 번호가 있으면 예외를 발생한다")
void createLottoFailWhenDuplicateNumberExists() {
- List lottoNumbers = Stream.of(1, 1, 2, 3, 4, 5)
+ Set lottoNumbers = Stream.of(1, 1, 2, 3, 4, 5)
.map(LottoNumber::valueOf)
- .toList();
+ .collect(toSet());
assertThatThrownBy(() -> new Lotto(lottoNumbers))
.isInstanceOf(IllegalArgumentException.class)
- .hasMessage("로또 번호는 중복되지 않아야 합니다.");
+ .hasMessage("로또 번호는 중복되지 않은 6개여야 합니다.\n");
}
}
@@ -68,26 +69,17 @@ class LottoCreationFromIntegerTest {
@DisplayName("로또를 생성한다")
void createLottoSuccess() {
- assertThatCode(() -> Lotto.from(List.of(1, 2, 3, 4, 5, 6)))
+ assertThatCode(() -> Lotto.from(Set.of(1, 2, 3, 4, 5, 6)))
.doesNotThrowAnyException();
}
@Test
- @DisplayName("로또를 생성할 때 6개의 로또 번호가 아니면 예외를 발생한다")
+ @DisplayName("로또를 생성할 때 중복되지 않은 6개의 로또 번호가 아니면 예외를 발생한다")
void createLottoFailWhenSizeIsNotSix() {
- assertThatThrownBy(() -> Lotto.from(List.of(1)))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("로또 번호는 6개여야 합니다.");
- }
-
- @Test
- @DisplayName("로또를 생성할 때 중복된 로또 번호가 있으면 예외를 발생한다")
- void createLottoFailWhenDuplicateNumberExists() {
-
- assertThatThrownBy(() -> Lotto.from(List.of(1, 1, 3, 4, 5, 6)))
+ assertThatThrownBy(() -> Lotto.from(Set.of(1)))
.isInstanceOf(IllegalArgumentException.class)
- .hasMessage("로또 번호는 중복되지 않아야 합니다.");
+ .hasMessage("로또 번호는 중복되지 않은 6개여야 합니다.\n");
}
}
@@ -113,8 +105,8 @@ class LottoComparisonTest {
@DisplayName("일치하는 로또 번호의 개수를 반환한다")
void countMatchingNumber() {
- Lotto lotto = Lotto.from(List.of(1, 2, 3, 4, 5, 6));
- Lotto compareLotto = Lotto.from(List.of(1, 2, 3, 7, 8, 9));
+ Lotto lotto = Lotto.from(Set.of(1, 2, 3, 4, 5, 6));
+ Lotto compareLotto = Lotto.from(Set.of(1, 2, 3, 7, 8, 9));
assertThat(lotto.countMatchingNumber(compareLotto)).isEqualTo(3);
}
@@ -128,7 +120,7 @@ class LottoContainsTest {
@DisplayName("로또 번호가 포함되어 있는지 확인한다")
void containsNumber() {
- Lotto lotto = Lotto.from(List.of(1, 2, 3, 4, 5, 6));
+ Lotto lotto = Lotto.from(Set.of(1, 2, 3, 4, 5, 6));
LottoNumber lottoNumber = LottoNumber.valueOf(3);
assertThat(lotto.containsNumber(lottoNumber)).isTrue();
diff --git a/src/test/java/org/duckstudy/domain/lotto/LottosTest.java b/src/test/java/org/duckstudy/domain/lotto/LottosTest.java
new file mode 100644
index 00000000..f5681802
--- /dev/null
+++ b/src/test/java/org/duckstudy/domain/lotto/LottosTest.java
@@ -0,0 +1,87 @@
+package org.duckstudy.domain.lotto;
+
+import static java.util.Map.entry;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.duckstudy.domain.lotto.constant.WinningRank.FIRST;
+import static org.duckstudy.domain.lotto.constant.WinningRank.NONE;
+import static org.duckstudy.domain.lotto.constant.WinningRank.SECOND;
+
+import java.util.List;
+import java.util.Set;
+import org.duckstudy.domain.Price;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+@DisplayName("로또 테스트")
+class LottosTest {
+
+ @Nested
+ @DisplayName("로또 묶음 생성 테스트")
+ class LottosCreationTest {
+
+ @Test
+ @DisplayName("구매 개수를 입력하면 가격에 맞는 로또 묶음을 생성한다")
+ void createLottosWhenInputPrice() {
+
+ Price purchasePrice = new Price(10000);
+ int lottoCount = purchasePrice.calculateLottoCount()
+ .getCount();
+
+ Lottos lottos = Lottos.generateLottos(lottoCount);
+
+ assertThat(lottos.getLottos()).hasSize(10);
+ }
+ }
+
+ @Nested
+ @DisplayName("로또 당첨 결과 테스트")
+ class LottosWinningTest {
+
+ @Test
+ @DisplayName("당첨된 로또 번호와 보너스 번호를 입력하면 당첨된 로또 묶음의 결과를 계산한다")
+ void calculateWinningResultWhenInputWinningLotto() {
+
+ Lotto winningLotto = Lotto.from(Set.of(1, 2, 3, 4, 5, 6));
+ LottoNumber bonusNumber = LottoNumber.valueOf(7);
+ Lottos totalLottos = new Lottos(List.of(
+ winningLotto,
+ Lotto.from(Set.of(1, 2, 3, 4, 5, 7)),
+ Lotto.from(Set.of(8, 9, 10, 11, 12, 13)),
+ Lotto.from(Set.of(14, 15, 16, 17, 18, 19)),
+ Lotto.from(Set.of(20, 21, 22, 23, 24, 25))
+ ));
+
+ LottoResult lottoResult = totalLottos.accumulateLottoResult(winningLotto, bonusNumber);
+
+ assertThat(lottoResult.getResult())
+ .containsExactly(
+ entry(NONE, 3),
+ entry(SECOND, 1),
+ entry(FIRST, 1)
+ );
+ }
+ }
+
+ @Nested
+ @DisplayName("로또 묶음 병합 테스트")
+ class LottosMergeTest {
+
+ @Test
+ @DisplayName("로또 묶음을 병합하면 하나의 로또 묶음으로 생성한다")
+ void mergeLottos() {
+
+ Lottos lottos1 = new Lottos(List.of(
+ Lotto.from(Set.of(1, 2, 3, 4, 5, 6))
+ ));
+
+ Lottos lottos2 = new Lottos(List.of(
+ Lotto.from(Set.of(7, 8, 9, 10, 11, 12))
+ ));
+
+ Lottos mergedLottos = lottos1.merge(lottos2);
+
+ assertThat(mergedLottos.getLottos()).hasSize(2);
+ }
+ }
+}
diff --git a/src/test/java/org/duckstudy/domain/lotto/constant/WinningRankTest.java b/src/test/java/org/duckstudy/domain/lotto/constant/WinningRankTest.java
new file mode 100644
index 00000000..c14997d4
--- /dev/null
+++ b/src/test/java/org/duckstudy/domain/lotto/constant/WinningRankTest.java
@@ -0,0 +1,33 @@
+package org.duckstudy.domain.lotto.constant;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.stream.Stream;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@DisplayName("당첨 순위 테스트")
+class WinningRankTest {
+
+ @DisplayName("당첨 횟수와 보너스 볼 여부를 입력하면 당첨 순위를 반환한다")
+ @ParameterizedTest
+ @MethodSource("generateMatchCountAndMatchBonus")
+ void findWinningRankSuccess(int matchCount, boolean matchBonus,
+ WinningRank expected) {
+ assertThat(WinningRank.findByMatchCountAndBonus(matchCount, matchBonus))
+ .isEqualTo(expected);
+ }
+
+ static Stream generateMatchCountAndMatchBonus() {
+ return Stream.of(
+ Arguments.of(0, false, WinningRank.NONE),
+ Arguments.of(3, false, WinningRank.FIFTH),
+ Arguments.of(4, false, WinningRank.FOURTH),
+ Arguments.of(5, false, WinningRank.THIRD),
+ Arguments.of(5, true, WinningRank.SECOND),
+ Arguments.of(6, false, WinningRank.FIRST)
+ );
+ }
+}
diff --git a/src/test/java/org/duckstudy/model/lotto/LottosTest.java b/src/test/java/org/duckstudy/model/lotto/LottosTest.java
deleted file mode 100644
index d71b4715..00000000
--- a/src/test/java/org/duckstudy/model/lotto/LottosTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.duckstudy.model.lotto;
-
-import static java.util.Map.entry;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.duckstudy.model.lotto.constant.WinningRank.FIRST;
-import static org.duckstudy.model.lotto.constant.WinningRank.NONE;
-import static org.duckstudy.model.lotto.constant.WinningRank.SECOND;
-
-import java.util.List;
-import org.duckstudy.model.Price;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-@DisplayName("로또 테스트")
-class LottosTest {
-
- @Nested
- @DisplayName("로또 묶음 생성 테스트")
- class LottosCreationTest {
-
- @Test
- @DisplayName("구매 가격을 입력하면 가격에 맞는 로또 묶음을 생성한다")
- void createLottosWhenInputPrice() {
-
- Price purchasePrice = new Price(10000);
-
- Lottos lottos = Lottos.generateLottosByPrice(purchasePrice);
-
- assertThat(lottos.getLottos()).hasSize(10);
- }
- }
-
- @Nested
- @DisplayName("로또 당첨 결과 테스트")
- class LottosWinningTest {
-
- @Test
- @DisplayName("당첨된 로또 번호와 보너스 번호를 입력하면 당첨된 로또 묶음의 결과를 계산한다")
- void calculateWinningResultWhenInputWinningLotto() {
-
- Lotto winningLotto = Lotto.from(List.of(1, 2, 3, 4, 5, 6));
- LottoNumber bonusNumber = LottoNumber.valueOf(7);
- Lottos totalLottos = new Lottos(List.of(
- winningLotto,
- Lotto.from(List.of(1, 2, 3, 4, 5, 7)),
- Lotto.from(List.of(8, 9, 10, 11, 12, 13)),
- Lotto.from(List.of(14, 15, 16, 17, 18, 19)),
- Lotto.from(List.of(20, 21, 22, 23, 24, 25))
- ));
-
- LottoResult lottoResult = totalLottos.accumulateLottoResult(winningLotto, bonusNumber);
-
- assertThat(lottoResult.getResult())
- .containsExactly(
- entry(NONE.getKey(), 3),
- entry(SECOND.getKey(), 1),
- entry(FIRST.getKey(), 1)
- );
- }
- }
-}