diff --git a/README.md b/README.md index 6c61bbed0a..b9b1aa2013 100644 --- a/README.md +++ b/README.md @@ -125,16 +125,120 @@ `WinningLotto` : Lotto 에서 일치갯수와 보너스 맞는지 받아와 Rank 에 전달하여 당첨 결과를 WinningResult 에 전달 `Rank` : 규칙 정의 및 등수 판단 -### 별도 메오 +### 별도 메모 - `Rank` 와 `Lotto` 순환참조 없애기 - indent 가 2여서 그러는건데 이 경우에 스트림 쓰기 추천 - `bonus` 가 모든 번호 검증 -> 필요한거만 - 쓰지않는 패키지(ctl + opt + O)와, 포매팅 정리 (opt + cmd + L) - ### 추가 개인 과제 -- 난수생성에 대한 테스트 통제 +- [x] 난수생성에 대한 테스트 통제 (보류) + - 현재 난수생성 방식은 static 메서드로 구현하여서 테스트 코드 내에서 Override가 불가능하다. ## 3차 피드백 후 리팩터링 - [x] : `LottoNumber`는 매번 인스턴스가 생성되기 때문에 인스턴스의 갯수가 너무 많아져 성능이 떨어질 수 있다. LottoNumber 인스턴스를 생성한 후 '재사용'할 수 있도록 구현한다. Map과 같은 곳에 인스턴스를 생성한 후 재사용하는 방법을 찾으면 됨 - -> 방식 : 캐싱 구현 \ No newline at end of file + -> 방식 : 캐싱 구현 + +#### 객체 그래프 +```text + Pay → LottoGame + ↘ + LottoTickets → List → List ← LottoNumberCache + ↘ + WinningLotto (Lotto + bonus LottoNumber) + ↓ + Rank → WinningResult (Map) +``` + +- 최종 + - `Pay` : 입력받은 지불 금액에 대한 객체 + - `LottoGame` : 로또 진행 흐름을 담당 (입력받은 금액을 갯수로 바꾼다.) + - `LottoTickets` : 입력받은 갯수에 따라 `Lotto` 객체 생성 + - `Lotto` : 로또 역할, 입력받은 번호가 몇개 일치하는지 확인 + - `LottoNumber` : 로또의 각 수에 대한 포장 객체 + - `LottoNumberCache` : 로또 번호를 캐싱하여 로또번호 재생산 막는 역할 + - `WinningLotto` : 당첨번호 관리 (Lotto + LottoNumber) + - `Rank` : 당첨 수와 금액의 정보를 가지고, 일치 갯수를 가지고 등수를 결정 + - `WinningResult` : 당첨 당첨 통계 추리기 + + + +# 4단계 - 로또 (수동) +## 기본 안내 +### 기능 요구사항 +- 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다. +- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다. + +## 핵심 요구 사항 +- 구매 로또 시 수동으로 추첨하는 기능 추가 +- 자동 생성 숫자, 수동 생성 번호 추가 + +## 기능목록 +- [x] : 수동으로 구매 할 로또 수 입력 + - [x] : 수동으로 구매하는 로또가 총 구매가능 수 보다 적은지 + - [x] :로또 곧바로 자동 생성이 아닌 총 구매 가능 수 제시 + - [x] : 넘었을 경우 다시 입력 유도 +- [x] : 수동으로 구매할 로또 번호 입력 + - [x] : 수동 로또 수만큼 반복입력한다 + - [x] : 중복 입력, 1 ~ 45 범위 넘겨서 입력 시 다시 입력 유도 + - 고민 : 입력 한번 할 때 마다 제시 or 모아서 넘기기 + - [x] : 모아서 넘기기 +- [x] : 로또 발행 결과 수동, 자동 갯수 카운트 + - [x] : 수동부터 먼저 제시 + +## 클래스 다이어그램 설계 +### 도메인 목록과 역할 + +#### 기존 +- `Pay` : 입력받은 지불 금액에 대한 객체 +- `LottoGame` : 로또 진행 흐름을 담당 (입력받은 금액을 갯수로 바꾼다.) +- `LottoTickets` : 입력받은 갯수에 따라 `Lotto` 객체 생성 +- `Lotto` : 로또 역할, 입력받은 번호가 몇개 일치하는지 확인 +- `LottoNumber` : 로또의 각 수에 대한 포장 객체 +- `LottoNumberCache` : 로또 번호를 캐싱하여 로또번호 재생산 막는 역할 +- `WinningLotto` : 당첨번호 관리 (Lotto + LottoNumber) +- `Rank` : 당첨 수와 금액의 정보를 가지고, 일치 갯수를 가지고 등수를 결정 +- `WinningResult` : 당첨 당첨 통계 추리기 + +#### 추가 +- `BuyLotto` : 로또 구매(자동 + 수동) 에 대한 관리 +- `manualLotto` : 수동구매하는 로또와 그 갯수 관리 + +## 1차 피드백 후 리팩터링 + +- [x] : 로또를 생성하는 부분 인터페이스로 분리해 보는 연습 +- [x] : of 메서드를 활용하면 LottoNumber 객체가 재사용되는 것을 보장하지 못함 + - `LottoNumber` 객체가 캐싱을 통해 재사용하는 것을 강제할 수 있도록 리팩터링 + - `LottoNumber`와 `LottoNumberCache`를 통합 + +## 2차 피드백 후 리팩터링 + +- [x] : 오히려 복잡해진 인터페이스 분리를 단순화하여서 구현 + - 이유 + - + 1. 이전에는 인터페이스 구현체들은 필드가 모두 같아야한다는 것으로 잘못된 이해를 한점 + + - 그 이유는 인터페이스 이용을 스프링 bean 생성 때만 주로 해봤기 때문 + - + 2. 필드가 같아야한다는 잘못된 이해로 메서드 인자는 모두 같게, 그 내부 로직을 달리해서 구현한 점 + - 이러한 이유 때문에 오히려 인터페이스가 더 복잡해짐 +- [x] : Controller 에서 분기 처리에 따라 인터페이스 구현체 갈아끼우는 것 제거하기 + - 조건문으로 같은 일(모드, 도메인)의 규칙 갈아끼우기에 불과함 +- 이렇게 인터페이스 분리하면 장점 + - 클라이언트 코드가 당장 필요한 구현체로 쉽게 갈아끼울수 있게 변경 + +## 3차 피드백 후 리팩터링 + +- [x] : LottoGenerator 가 LottoBuy 가 아닌 LottoTickets 을 생성하게 하기 +- [x] : LottoGenerator 구현체에서 바로 LottoTickets를 생성하도록 접근하기 +- [x] : LottoManulGenerator 에서 불필요한 pay 제거 + - 자동, 수동, 혼합 간에 필드는 비슷하게 pay, pay + lotto, lotto 각각 상황에 따라 인자가 달라짐 + - 즉 충분히 필드가 다를 수있다. + +## 4차 피드백 후 리팩터링 + +- [x] : 이미 `LottoAutoGenerator`, `LottoManualGenerator` 각각의 구현체가 있는데 굳이 수동 자동관련 `Auto`, `Manual` 객체가 필요한지 고민할 것 + - 고민 : 기존에는 Auto, Manual 이 필요할 수있었다. 그러나 인터페이스 구현체가 그 역할을 대체한다 +- [ ] : composite 패턴을 이용해 part 인 구현체 `LottoAutoGenerator`, `LottoManualGenerator` 를 합친 composite 인 `LottoCombineGenerator` 를 이용한다 + - 그러면 필드도 두개 구현체를 합친 인터페이스의 List로 한다 그리고 기존 생성도 두개를 각각 넣어주는 방식으로 구현한다 + - 즉 여러 LottoGenerator 를 묶어서 두개를 하나처럼 쓸 수 있게 됨 \ No newline at end of file diff --git a/src/main/java/lotto/controller/LottoApplication.java b/src/main/java/lotto/controller/LottoApplication.java index 5a5358b5a0..beddbc4076 100644 --- a/src/main/java/lotto/controller/LottoApplication.java +++ b/src/main/java/lotto/controller/LottoApplication.java @@ -1,6 +1,10 @@ package lotto.controller; +import java.util.List; +import java.util.stream.IntStream; import lotto.domain.business.LottoGame; +import lotto.domain.model.LottoGenertor.LottoCombineGenerator; +import lotto.domain.model.LottoGenertor.LottoGenerator; import lotto.domain.model.WinningResult; import lotto.view.InputView; import lotto.view.ResultView; @@ -8,15 +12,46 @@ public class LottoApplication { public static void main(String[] args) { - int pay = InputView.inputPurchaseAmount(); + new LottoApplication().run(); + } + + private void run() { + while(true) { + try { + play(); + return; + } catch(IllegalArgumentException e) { + ResultView.printError(e.getMessage()); + } + } + } + + private void play() { + String pay = InputView.inputPurchaseAmount(); + String manualCount = InputView.inputManulNumber(); - LottoGame lottoGame = new LottoGame(pay); + List manualLottoNumbers = readManualLottos(manualCount); + LottoGenerator lottoGenerator = new LottoCombineGenerator(pay, manualLottoNumbers); + + ResultView.printAutoManualCount(lottoGenerator.getBuyCount()); + LottoGame lottoGame = new LottoGame(pay, lottoGenerator.generateTickets()); ResultView.printLottos(lottoGame.getLottos()); - String winningNumbers = InputView.inputWinningNumbers(); - int bonusNumbers = InputView.inputBonusNumbers(); - WinningResult winningResult = lottoGame.calculateWinningResult(winningNumbers, bonusNumbers); + WinningResult winningResult = lottoGame.calculateWinningResult(InputView.inputWinningNumbers(), InputView.inputBonusNumbers()); String totalReturn = winningResult.calculateTotalReturn(pay); ResultView.printResult(winningResult, totalReturn); } + + private List readManualLottos(String count) { + if(count.isEmpty()) { + throw new IllegalArgumentException("수동 구매 수는 0 이상의 숫자로 입력해 주세요."); + } + int manualCount = Integer.parseInt(count); + if(manualCount > 0) { + InputView.inputManulMessage(); + } + return IntStream.range(0, manualCount) + .mapToObj(i -> InputView.inputManulLotto()) + .toList(); + } } diff --git a/src/main/java/lotto/domain/business/LottoGame.java b/src/main/java/lotto/domain/business/LottoGame.java index 7331691ce7..052fe33b9d 100644 --- a/src/main/java/lotto/domain/business/LottoGame.java +++ b/src/main/java/lotto/domain/business/LottoGame.java @@ -5,8 +5,20 @@ public record LottoGame(Pay pay, LottoTickets lottoTickets) { + public LottoGame(String pay, LottoTickets lottoTickets) { + this(new Pay(pay), lottoTickets); + } + public LottoGame(int pay) { - this(new Pay(pay), generateLottos(new Pay(pay))); + this(new Pay(pay)); + } + + public LottoGame(int pay, LottoTickets lottoTickets) { + this(new Pay(pay), lottoTickets); + } + + private LottoGame(Pay pay) { + this(pay, generateLottos(pay)); } private static LottoTickets generateLottos(Pay pay) { @@ -20,4 +32,8 @@ public List getLottos() { public WinningResult calculateWinningResult(String winnerLottoNumber, int bonus) { return this.lottoTickets.identifyWinners(new WinningLotto(bonus, winnerLottoNumber)); } + + public WinningResult calculateWinningResult(String winnerLottoNumber, String bonus) { + return calculateWinningResult(winnerLottoNumber, Integer.parseInt(bonus)); + } } diff --git a/src/main/java/lotto/domain/model/BuyCount.java b/src/main/java/lotto/domain/model/BuyCount.java new file mode 100644 index 0000000000..9d8734b53d --- /dev/null +++ b/src/main/java/lotto/domain/model/BuyCount.java @@ -0,0 +1,27 @@ +package lotto.domain.model; + +public record BuyCount(int total, int manual, int auto) { + + public BuyCount { + validate(total, manual, auto); + } + + private void validate(int total, int manual, int auto) { + if(total != (manual + auto)) { + throw new IllegalArgumentException("지불금액 대비 구매수가 일치하지 않습니다."); + } + } + + public BuyCount add(BuyCount other) { + return new BuyCount( + this.total + other.total, + this.manual + other.manual, + this.auto + other.auto + ); + } + + public static BuyCount empty() { + return new BuyCount(0, 0, 0); + } + +} diff --git a/src/main/java/lotto/domain/model/Lotto.java b/src/main/java/lotto/domain/model/Lotto.java index 0804765b5e..aa71808333 100644 --- a/src/main/java/lotto/domain/model/Lotto.java +++ b/src/main/java/lotto/domain/model/Lotto.java @@ -49,7 +49,7 @@ private static List getIntegers(int[] ints) { private static List generateLottoNumberByInput(String lottoNumbers) { return Arrays.stream(extractWinnerLottoNumber(lottoNumbers)) - .map(LottoNumberCache::getLottoNumber) + .map(LottoNumber::of) .toList(); } @@ -77,13 +77,13 @@ private static List generateNumberList() { private static List convertToLottoNumbers(List lottoNumbers) { return lottoNumbers.stream() - .map(LottoNumberCache::getLottoNumber) + .map(LottoNumber::of) .toList(); } public List numberValues() { return this.numbers.stream() - .map(LottoNumber::value) + .map(LottoNumber::getValue) .toList(); } diff --git a/src/main/java/lotto/domain/model/LottoGenertor/LottoAutoGenerator.java b/src/main/java/lotto/domain/model/LottoGenertor/LottoAutoGenerator.java new file mode 100644 index 0000000000..2e3b26eda5 --- /dev/null +++ b/src/main/java/lotto/domain/model/LottoGenertor/LottoAutoGenerator.java @@ -0,0 +1,42 @@ +package lotto.domain.model.LottoGenertor; + +import java.util.List; +import java.util.stream.IntStream; +import lotto.domain.model.BuyCount; +import lotto.domain.model.Lotto; +import lotto.domain.model.LottoTickets; +import lotto.domain.model.Pay; + +public class LottoAutoGenerator implements LottoGenerator { + + int pay; + + public LottoAutoGenerator(String pay) { + this(Integer.parseInt(pay)); + } + + public LottoAutoGenerator(int pay) { + this.pay = pay; + } + + @Override + public LottoTickets generateTickets() { + return new LottoTickets(generateLottos(getTotalNumber(pay)).stream().toList()); + } + + private List generateLottos(int num) { + return IntStream + .range(0, num) + .mapToObj(i -> new Lotto()) + .toList(); + } + + @Override + public BuyCount getBuyCount() { + return new BuyCount(getTotalNumber(pay), 0, getTotalNumber(pay)); + } + + private int getTotalNumber(int pay) { + return new Pay(pay).convertToBuyCount(); + } +} diff --git a/src/main/java/lotto/domain/model/LottoGenertor/LottoCombineGenerator.java b/src/main/java/lotto/domain/model/LottoGenertor/LottoCombineGenerator.java new file mode 100644 index 0000000000..867eae66da --- /dev/null +++ b/src/main/java/lotto/domain/model/LottoGenertor/LottoCombineGenerator.java @@ -0,0 +1,52 @@ +package lotto.domain.model.LottoGenertor; + +import java.util.List; +import java.util.stream.Stream; +import lotto.domain.model.BuyCount; +import lotto.domain.model.LottoTickets; +import lotto.domain.model.Pay; + +public class LottoCombineGenerator implements LottoGenerator { + + private final List generators; + + public LottoCombineGenerator(int pay, String manualLottoNumbers) { + this(pay, List.of(manualLottoNumbers)); + } + + public LottoCombineGenerator(String pay, List manualLottoNumbers) { + this(Integer.parseInt(pay), manualLottoNumbers); + } + + public LottoCombineGenerator(int pay, List manualLottoNumbers) { + this(toLottosGenerators(pay, manualLottoNumbers)); + } + + public LottoCombineGenerator(List generators) { + this.generators = generators; + } + + @Override + public LottoTickets generateTickets() { + return new LottoTickets(generators. + stream() + .map(LottoGenerator::generateTickets) + .flatMap(t -> t.tickets().stream()) + .toList()); + } + + private static List toLottosGenerators(int pay, List manualLottoNumbers) { + int remainPay = new Pay(pay).calculateRemainingPayment(manualLottoNumbers.size()); + return Stream.of( + new LottoManulGenerator(manualLottoNumbers), + new LottoAutoGenerator(remainPay)) + .toList(); + } + + @Override + public BuyCount getBuyCount() { + return generators.stream() + .map(LottoGenerator::getBuyCount) + .reduce(BuyCount.empty(), BuyCount::add); + } +} diff --git a/src/main/java/lotto/domain/model/LottoGenertor/LottoGenerator.java b/src/main/java/lotto/domain/model/LottoGenertor/LottoGenerator.java new file mode 100644 index 0000000000..eb5dc7a31a --- /dev/null +++ b/src/main/java/lotto/domain/model/LottoGenertor/LottoGenerator.java @@ -0,0 +1,11 @@ +package lotto.domain.model.LottoGenertor; + +import lotto.domain.model.BuyCount; +import lotto.domain.model.LottoTickets; + +public interface LottoGenerator { + + LottoTickets generateTickets(); + + BuyCount getBuyCount(); +} diff --git a/src/main/java/lotto/domain/model/LottoGenertor/LottoManulGenerator.java b/src/main/java/lotto/domain/model/LottoGenertor/LottoManulGenerator.java new file mode 100644 index 0000000000..3cfaec48ae --- /dev/null +++ b/src/main/java/lotto/domain/model/LottoGenertor/LottoManulGenerator.java @@ -0,0 +1,35 @@ +package lotto.domain.model.LottoGenertor; + +import java.util.List; +import lotto.domain.model.BuyCount; +import lotto.domain.model.Lotto; +import lotto.domain.model.LottoTickets; + +public class LottoManulGenerator implements LottoGenerator { + + private final List manualLottoNumbers; + + public LottoManulGenerator(String manualLottoNumbers) { + this(List.of(manualLottoNumbers)); + } + + public LottoManulGenerator(List manualLottoNumbers) { + this.manualLottoNumbers = toLottoList(manualLottoNumbers); + } + + @Override + public LottoTickets generateTickets() { + return new LottoTickets(manualLottoNumbers); + } + + @Override + public BuyCount getBuyCount() { + return new BuyCount(manualLottoNumbers.size(), manualLottoNumbers.size(), 0); + } + + private static List toLottoList(List lottoNumbers) { + return lottoNumbers.stream() + .map(Lotto::new) + .toList(); + } +} diff --git a/src/main/java/lotto/domain/model/LottoNumber.java b/src/main/java/lotto/domain/model/LottoNumber.java index e6b03b2d0e..7b5c8c86e3 100644 --- a/src/main/java/lotto/domain/model/LottoNumber.java +++ b/src/main/java/lotto/domain/model/LottoNumber.java @@ -1,18 +1,71 @@ package lotto.domain.model; -public record LottoNumber(int value) { +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class LottoNumber { + + private final int value; + private final static Map cache; + public static final int MIN_LOTTO_NUMBER = 1; + public static final int MAX_LOTTO_NUMBER = 45; - public LottoNumber(String value) { + static { + cache = getLottoNumberMap(); + } + + private static Map getLottoNumberMap() { + return IntStream.rangeClosed(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER) + .boxed() + .collect( + Collectors.toMap( + Function.identity(), + LottoNumber::new) + ); + } + + private LottoNumber(String value) { this(Integer.parseInt(value)); } - public LottoNumber { - validate(value); + private LottoNumber(int value) { + this.value = value; + } + + public static LottoNumber of(int value) { + LottoNumber cachedLottoNumber = cache.get(value); + validate(cachedLottoNumber); + return cachedLottoNumber; } - private void validate(int value) { - if(value < 1 || value > 45) { + public static LottoNumber of(String value) { + return of(Integer.parseInt(value)); + } + + public int getValue() { + return value; + } + + private static void validate(LottoNumber result) { + if(result == null) { throw new IllegalArgumentException("로또 번호는 1~45사이 입력하시오"); } } + + @Override + public boolean equals(Object o) { + if(o == null || getClass() != o.getClass()) { + return false; + } + LottoNumber that = (LottoNumber) o; + return value == that.value; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } } diff --git a/src/main/java/lotto/domain/model/LottoNumberCache.java b/src/main/java/lotto/domain/model/LottoNumberCache.java deleted file mode 100644 index 6f1579fc9b..0000000000 --- a/src/main/java/lotto/domain/model/LottoNumberCache.java +++ /dev/null @@ -1,45 +0,0 @@ -package lotto.domain.model; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public class LottoNumberCache { - - private final static Map cache; - public static final int MIN_LOTTO_NUMBER = 1; - public static final int MAX_LOTTO_NUMBER = 45; - - static { - cache = getLottoNumberMap(); - } - - private static Map getLottoNumberMap() { - return IntStream.rangeClosed(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER) - .boxed() - .collect( - Collectors.toMap( - Function.identity(), - LottoNumber::new) - ); - } - - public static LottoNumber getLottoNumber(String lottoNumber) { - return getLottoNumber(Integer.parseInt(lottoNumber)); - } - - public static LottoNumber getLottoNumber(int lottoNumber) { - LottoNumber cachedLottoNumber = cache.get(lottoNumber); - validate(cachedLottoNumber); - return cachedLottoNumber; - } - - private static void validate(LottoNumber result) { - if(result == null) { - throw new IllegalArgumentException("로또 번호는 1~45사이 입력하시오"); - } - } -} diff --git a/src/main/java/lotto/domain/model/LottoTickets.java b/src/main/java/lotto/domain/model/LottoTickets.java index ba4f69a534..b3060611e0 100644 --- a/src/main/java/lotto/domain/model/LottoTickets.java +++ b/src/main/java/lotto/domain/model/LottoTickets.java @@ -14,6 +14,10 @@ public LottoTickets(int num) { this(generateLottos(num)); } + public int getLottoTicketCount() { + return tickets.size(); + } + private static List generateLottos(int num) { return IntStream .range(0, num) diff --git a/src/main/java/lotto/domain/model/Pay.java b/src/main/java/lotto/domain/model/Pay.java index 4659a43027..1bc19d308e 100644 --- a/src/main/java/lotto/domain/model/Pay.java +++ b/src/main/java/lotto/domain/model/Pay.java @@ -1,16 +1,28 @@ package lotto.domain.model; -public record Pay(int pay) { +public record Pay(int value) { public static final int LOTTO_PRICE = 1000; + public Pay(String pay) { + this(Integer.parseInt(pay)); + } + public Pay { - validate(pay); - positiveValidate(pay); + validate(value); + positiveValidate(value); } public int convertToBuyCount() { - return this.pay / LOTTO_PRICE; + return this.value / LOTTO_PRICE; + } + + public int calculateRemainingPayment(int count) { + int remainPay = (convertToBuyCount() - count) * LOTTO_PRICE; + if(remainPay < 0) { + positiveValidate(remainPay); + } + return remainPay; } private static void validate(int pay) { diff --git a/src/main/java/lotto/domain/model/WinningLotto.java b/src/main/java/lotto/domain/model/WinningLotto.java index e5eab05466..1df494dd5a 100644 --- a/src/main/java/lotto/domain/model/WinningLotto.java +++ b/src/main/java/lotto/domain/model/WinningLotto.java @@ -5,19 +5,19 @@ public record WinningLotto(LottoNumber bonusNumber, Lotto winNumbers) { public WinningLotto(int bonusNumber, int... winNumbers) { - this(LottoNumberCache.getLottoNumber(bonusNumber), new Lotto(winNumbers)); + this(LottoNumber.of(bonusNumber), new Lotto(winNumbers)); } public WinningLotto(String bonusNumber, int... winNumbers) { - this(LottoNumberCache.getLottoNumber(bonusNumber), new Lotto(winNumbers)); + this(LottoNumber.of(bonusNumber), new Lotto(winNumbers)); } public WinningLotto(String bonusNumber, String winNumbers) { - this(LottoNumberCache.getLottoNumber(bonusNumber), new Lotto(winNumbers)); + this(LottoNumber.of(bonusNumber), new Lotto(winNumbers)); } public WinningLotto(int bonusNumber, String winNumbers) { - this(LottoNumberCache.getLottoNumber(bonusNumber), new Lotto(winNumbers)); + this(LottoNumber.of(bonusNumber), new Lotto(winNumbers)); } public WinningLotto { diff --git a/src/main/java/lotto/domain/model/WinningResult.java b/src/main/java/lotto/domain/model/WinningResult.java index fe388623db..8432e0b980 100644 --- a/src/main/java/lotto/domain/model/WinningResult.java +++ b/src/main/java/lotto/domain/model/WinningResult.java @@ -28,6 +28,10 @@ public String calculateTotalReturn(int pay) { return rate.toPlainString(); } + public String calculateTotalReturn(String pay) { + return calculateTotalReturn(Integer.parseInt(pay)); + } + private BigDecimal getProfit() { BigDecimal profit = BigDecimal.ZERO; for(Entry matchIntegerEntry: winningRankCounts.entrySet()) { diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java index 64b0c5c9ec..75a439b7cb 100644 --- a/src/main/java/lotto/view/InputView.java +++ b/src/main/java/lotto/view/InputView.java @@ -6,18 +6,31 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); - public static int inputPurchaseAmount() { + public static String inputPurchaseAmount() { System.out.println("구입금액을 입력해 주세요."); - return Integer.parseInt(scanner.nextLine()); + return scanner.nextLine(); } - public static int inputBonusNumbers() { + public static String inputBonusNumbers() { System.out.println("보너스 볼을 입력해 주세요."); - return Integer.parseInt(scanner.nextLine()); + return scanner.nextLine(); } public static String inputWinningNumbers() { System.out.println("지난 주 당첨 번호를 입력해 주세요."); return scanner.nextLine(); } + + public static String inputManulNumber() { + System.out.println("수동으로 구매할 로또 수를 입력해 주세요."); + return scanner.nextLine(); + } + + public static void inputManulMessage() { + System.out.println("수동으로 구매할 번호를 입력해 주세요."); + } + + public static String inputManulLotto() { + return scanner.nextLine(); + } } diff --git a/src/main/java/lotto/view/ResultView.java b/src/main/java/lotto/view/ResultView.java index 384414e1e0..e07ac1a522 100644 --- a/src/main/java/lotto/view/ResultView.java +++ b/src/main/java/lotto/view/ResultView.java @@ -2,19 +2,22 @@ import java.util.List; import lotto.domain.Rank; +import lotto.domain.model.BuyCount; import lotto.domain.model.Lotto; import lotto.domain.model.WinningResult; public class ResultView { public static void printLottos(List lottoList) { - System.out.println(lottoList.size() + "개를 구매했습니다."); - for(Lotto lotto: lottoList) { System.out.println(lotto.numberValues()); } } + public static void printAutoManualCount(BuyCount buyCount) { + System.out.printf("수동으로 %d장, 자동으로 %d개를 구매했습니다.%n", buyCount.manual(), buyCount.auto()); + } + public static void printResult(WinningResult winningResult, String totalReturnRate) { System.out.println("당첨 통계"); System.out.println("---------"); @@ -29,4 +32,8 @@ public static void printResult(WinningResult winningResult, String totalReturnRa System.out.printf("총 수익률은 %s입니다.%n", totalReturnRate); } } + + public static void printError(String message) { + System.out.println(message); + } } diff --git a/src/test/java/lotto/domain/model/BuyCountTest.java b/src/test/java/lotto/domain/model/BuyCountTest.java new file mode 100644 index 0000000000..4bf556231b --- /dev/null +++ b/src/test/java/lotto/domain/model/BuyCountTest.java @@ -0,0 +1,30 @@ +package lotto.domain.model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.junit.jupiter.api.Test; + +class BuyCountTest { + + @Test + void 지불금액으로_만드는로또수가_실제로또수와_다르면_에러전파() { + + assertThatThrownBy(() -> { + new BuyCount(5, 1, 1); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage("지불금액 대비 구매수가 일치하지 않습니다."); + + } + + @Test + void 구매갯수를_누적해서_만든다() { + BuyCount buyCountBase = BuyCount.empty(); + BuyCount buyCountFirstAdd = buyCountBase.add(new BuyCount(5, 3, 2)); + BuyCount buyCountSecondAdd = buyCountFirstAdd.add(new BuyCount(15, 5, 10)); + + BuyCount expected = new BuyCount(20, 8, 12); + assertThat(buyCountSecondAdd).isEqualTo(expected); + + } +} \ No newline at end of file diff --git a/src/test/java/lotto/domain/model/LottoGenertor/LottoAutoGeneratorTest.java b/src/test/java/lotto/domain/model/LottoGenertor/LottoAutoGeneratorTest.java new file mode 100644 index 0000000000..4caa899cdd --- /dev/null +++ b/src/test/java/lotto/domain/model/LottoGenertor/LottoAutoGeneratorTest.java @@ -0,0 +1,18 @@ +package lotto.domain.model.LottoGenertor; + +import static org.assertj.core.api.Assertions.assertThat; + +import lotto.domain.model.LottoTickets; +import org.junit.jupiter.api.Test; + +class LottoAutoGeneratorTest { + + @Test + void 자동으로_생성한_만큼_보여준다() { + LottoGenerator lottoGenerator = new LottoAutoGenerator(5000); + + LottoTickets lottoTickets = lottoGenerator.generateTickets(); + assertThat(lottoTickets.getLottoTicketCount()).isEqualTo(5); + assertThat(lottoGenerator.getBuyCount().total()).isEqualTo(5); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/domain/model/LottoGenertor/LottoCombineGeneratorTest.java b/src/test/java/lotto/domain/model/LottoGenertor/LottoCombineGeneratorTest.java new file mode 100644 index 0000000000..ddc6baca0b --- /dev/null +++ b/src/test/java/lotto/domain/model/LottoGenertor/LottoCombineGeneratorTest.java @@ -0,0 +1,28 @@ +package lotto.domain.model.LottoGenertor; + +import static org.assertj.core.api.Assertions.assertThat; + +import lotto.domain.model.LottoTickets; +import org.junit.jupiter.api.Test; + +class LottoCombineGeneratorTest { + + @Test + void 수동으로_생성한_만큼_보여준다() { + LottoGenerator lottoGenerator = new LottoCombineGenerator(5000, "1,2,3,4,5,6"); + LottoTickets lottoTickets = lottoGenerator.generateTickets(); + + assertThat(lottoTickets).isNotNull(); + assertThat(lottoGenerator.getBuyCount().manual()).isEqualTo(1); + } + + @Test + void 자동으로_생성한_만큼_보여준다() { + LottoGenerator lottoGenerator = new LottoCombineGenerator(5000, "1,2,3,4,5,6"); + LottoTickets lottoTickets = lottoGenerator.generateTickets(); + + assertThat(lottoTickets).isNotNull(); + assertThat(lottoGenerator.getBuyCount().auto()).isEqualTo(4); + } + +} \ No newline at end of file diff --git a/src/test/java/lotto/domain/model/LottoGenertor/LottoManulGeneratorTest.java b/src/test/java/lotto/domain/model/LottoGenertor/LottoManulGeneratorTest.java new file mode 100644 index 0000000000..3132f4ff37 --- /dev/null +++ b/src/test/java/lotto/domain/model/LottoGenertor/LottoManulGeneratorTest.java @@ -0,0 +1,17 @@ +package lotto.domain.model.LottoGenertor; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import lotto.domain.model.LottoTickets; +import org.junit.jupiter.api.Test; + +class LottoManulGeneratorTest { + + @Test + void 수동으로_생성한_만큼_보여준다() { + LottoGenerator lottoGenerator = new LottoManulGenerator(List.of("1,2,3,4,5,6", "1,2,3,4,5,7", "1,2,3,4,5,9")); + LottoTickets lottoTickets = lottoGenerator.generateTickets(); + assertThat(lottoTickets.getLottoTicketCount()).isEqualTo(3); + } +} \ No newline at end of file diff --git a/src/test/java/lotto/domain/model/LottoNumberCacheTest.java b/src/test/java/lotto/domain/model/LottoNumberCacheTest.java deleted file mode 100644 index 73b584692b..0000000000 --- a/src/test/java/lotto/domain/model/LottoNumberCacheTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package lotto.domain.model; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -class LottoNumberCacheTest { - - @Test - void lottoNumber를_입력하면_필요한_LottoNumber객체를_반환한다() { - assertThat(LottoNumberCache.getLottoNumber(5)) - .isEqualTo(new LottoNumber(5)); - } - - @ParameterizedTest - @ValueSource(ints = {0, 46}) - void 로또번호가_범위_밖이면_에러(int num) { - assertThatThrownBy(() -> - LottoNumberCache.getLottoNumber(num) - ).isInstanceOf(IllegalArgumentException.class) - .hasMessage("로또 번호는 1~45사이 입력하시오"); - } -} \ No newline at end of file diff --git a/src/test/java/lotto/domain/model/LottoNumberTest.java b/src/test/java/lotto/domain/model/LottoNumberTest.java index ab07622c69..16921481c1 100644 --- a/src/test/java/lotto/domain/model/LottoNumberTest.java +++ b/src/test/java/lotto/domain/model/LottoNumberTest.java @@ -11,7 +11,7 @@ class LottoNumberTest { @ValueSource(ints = {0, 46}) void 로또번호가_범위_밖이면_에러(int num) { assertThatThrownBy(() -> - new LottoNumber(num) + LottoNumber.of(num) ).isInstanceOf(IllegalArgumentException.class) .hasMessage("로또 번호는 1~45사이 입력하시오"); } diff --git a/src/test/java/lotto/domain/model/LottoTest.java b/src/test/java/lotto/domain/model/LottoTest.java index eb557583ce..5dd778747d 100644 --- a/src/test/java/lotto/domain/model/LottoTest.java +++ b/src/test/java/lotto/domain/model/LottoTest.java @@ -1,6 +1,5 @@ package lotto.domain.model; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.junit.jupiter.api.Test; diff --git a/src/test/java/lotto/domain/model/LottoTicketsTest.java b/src/test/java/lotto/domain/model/LottoTicketsTest.java index 17a3ef06fc..de03301f43 100644 --- a/src/test/java/lotto/domain/model/LottoTicketsTest.java +++ b/src/test/java/lotto/domain/model/LottoTicketsTest.java @@ -7,12 +7,6 @@ class LottoTicketsTest { - @Test - void 갯수를_입력받으면_로또를_생성한다() { - LottoTickets lottoTickets = new LottoTickets(10); - assertThat(lottoTickets.tickets()).hasSize(10); - } - @Test void 당첨번호를_입력받으면_당첨번호와_몇개_당첨인지_알린다() { LottoTickets lottoTickets = new LottoTickets(new Lotto(1, 2, 3, 4, 5, 6), new Lotto(40, 41, 42, 43, 44, 45)); @@ -28,7 +22,7 @@ class LottoTicketsTest { void 당첨2등과_3등이면_무엇인지_알려준다() { LottoTickets lottoTickets = new LottoTickets(new Lotto(10, 41, 42, 43, 44, 45), new Lotto(20, 41, 42, 43, 44, 45)); - WinningResult winningResult = lottoTickets.identifyWinners(new WinningLotto(LottoNumberCache.getLottoNumber(20), new Lotto(35, 41, 42, 43, 44, 45))); + WinningResult winningResult = lottoTickets.identifyWinners(new WinningLotto(LottoNumber.of(20), new Lotto(35, 41, 42, 43, 44, 45))); WinningResult expectedWinner = new WinningResult(); expectedWinner.recordRank(Rank.SECOND); expectedWinner.recordRank(Rank.THIRD); diff --git a/src/test/java/lotto/domain/model/PayTest.java b/src/test/java/lotto/domain/model/PayTest.java index edff53c913..b127c94d68 100644 --- a/src/test/java/lotto/domain/model/PayTest.java +++ b/src/test/java/lotto/domain/model/PayTest.java @@ -29,4 +29,10 @@ class PayTest { ).isInstanceOf(IllegalArgumentException.class) .hasMessage("금액은 양수로 입력하시오"); } + + @Test + void 수동_3개_구매후_남은_금액계산은_3000원_빠진다() { + Pay pay = new Pay(10000); + assertThat(pay.calculateRemainingPayment(3)).isEqualTo(7000); + } } \ No newline at end of file diff --git a/src/test/java/lotto/domain/model/WinningLottoTest.java b/src/test/java/lotto/domain/model/WinningLottoTest.java index f7a6518a9e..f752b2c430 100644 --- a/src/test/java/lotto/domain/model/WinningLottoTest.java +++ b/src/test/java/lotto/domain/model/WinningLottoTest.java @@ -11,21 +11,21 @@ class WinningLottoTest { @Test void 당첨번호와_보너스번호는_같으면_에러전파() { assertThatThrownBy(() -> - new WinningLotto(LottoNumberCache.getLottoNumber(1), new Lotto(1, 2, 3, 4, 5, 6)) + new WinningLotto(LottoNumber.of(1), new Lotto(1, 2, 3, 4, 5, 6)) ).isInstanceOf(IllegalArgumentException.class) .hasMessage("당첨번호 일부가 보너스 번호가 같습니다"); } @Test public void 로또번호4개_일치하면_FOURTH가_반환된다() { - WinningLotto winningLotto = new WinningLotto(LottoNumberCache.getLottoNumber(20), new Lotto(42, 36, 37, 43, 44, 45)); + WinningLotto winningLotto = new WinningLotto(LottoNumber.of(20), new Lotto(42, 36, 37, 43, 44, 45)); Rank rank = winningLotto.rankDecide(new Lotto(40, 41, 42, 43, 44, 45)); assertThat(rank).isEqualTo(Rank.FOURTH); } @Test public void 로또번호6개_일치하면_FIRST가_반환된다() { - WinningLotto winningLotto = new WinningLotto(LottoNumberCache.getLottoNumber(20), new Lotto(42, 41, 40, 43, 44, 45)); + WinningLotto winningLotto = new WinningLotto(LottoNumber.of(20), new Lotto(42, 41, 40, 43, 44, 45)); Rank rank = winningLotto.rankDecide(new Lotto(40, 41, 42, 43, 44, 45)); assertThat(rank).isEqualTo(Rank.FIRST); }