From ba49cb48f51685715e367f17603e18d7210bd2b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=EC=A4=80=ED=97=8C?= Date: Sun, 6 Apr 2025 19:33:05 +0900 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20=F0=9F=9A=80=204=EB=8B=A8=EA=B3=84?= =?UTF-8?q?=20-=20=EB=A1=9C=EB=98=90(=EC=88=98=EB=8F=99)=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20=EC=82=AC=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\353\230\220(\354\210\230\353\217\231).md" | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 "\360\237\232\200 4\353\213\250\352\263\204 - \353\241\234\353\230\220(\354\210\230\353\217\231).md" diff --git "a/\360\237\232\200 4\353\213\250\352\263\204 - \353\241\234\353\230\220(\354\210\230\353\217\231).md" "b/\360\237\232\200 4\353\213\250\352\263\204 - \353\241\234\353\230\220(\354\210\230\353\217\231).md" new file mode 100644 index 0000000000..081e5fd541 --- /dev/null +++ "b/\360\237\232\200 4\353\213\250\352\263\204 - \353\241\234\353\230\220(\354\210\230\353\217\231).md" @@ -0,0 +1,65 @@ +## 기능 요구사항 +- 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다. +- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다. +```powershell +구입금액을 입력해 주세요. +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이기 때문에 결과적으로 손해라는 의미임) +``` +## 프로그래밍 요구사항 +- 규칙 3: 모든 원시값과 문자열을 포장한다. +- 규칙 5: 줄여쓰지 않는다(축약 금지). +- 예외 처리를 통해 에러가 발생하지 않도록 한다. +- 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 +- java enum을 적용해 프로그래밍을 구현한다. +- 규칙 8: 일급 콜렉션을 쓴다. +- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. +- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. +- 자바 코드 컨벤션을 지키면서 프로그래밍한다. +- else 예약어를 쓰지 않는다. +### 힌트 +- 규칙 3: 모든 원시값과 문자열을 포장한다. + - 로또 숫자 하나는 int 타입이다. 이 숫자 하나를 추상화한 LottoNo 객체를 추가해 구현한다. +- 예외 처리를 통해 에러가 발생하지 않도록 한다. + - 사용자가 잘못된 값을 입력했을 때 java exception으로 에러 처리를 한다. + - java8에 추가된 Optional을 적용해 NullPointerException이 발생하지 않도록 한다. +## 기능 목록 및 commit 로그 요구사항 +- 기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리해 추가한다. +- git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다. \ No newline at end of file From c79cbe0da23856357368675b63db0229975292a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=95=88=EC=A4=80=ED=97=8C?= Date: Sun, 6 Apr 2025 21:17:25 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feature:=20=EB=A1=9C=EB=98=90=20=EC=88=98?= =?UTF-8?q?=EB=8F=99=20=EC=9E=85=EB=A0=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lotto/domain/entity/LottoTickets.java | 14 ++++- .../domain/entity/WinningStatistics.java | 20 +------ .../LottoCountCannotBeNegativeException.java | 10 ++++ .../LottoNumberInputRegexException.java | 10 ++++ .../domain/strategy/LottoAutoPicker.java | 20 +++---- .../camp/lotto/domain/vo/LottoAmount.java | 8 +-- .../camp/lotto/domain/vo/LottoCount.java | 50 +++++++++++++++++ .../camp/lotto/domain/vo/LottoNumber.java | 8 +-- .../camp/lotto/domain/vo/LottoNumbers.java | 10 ++-- .../camp/lotto/view/LottoInputView.java | 15 ++--- .../view/component/LottoManuelCountInput.java | 21 +++++++ .../view/component/LottoTicketsAutoInput.java | 34 +++++++++++ .../view/component/LottoTicketsInput.java | 9 +-- .../component/LottoTicketsManualInput.java | 21 +++++++ .../strategy/BonusNumberInputStrategy.java | 8 +-- .../LottoManuelCountInputStrategy.java | 29 ++++++++++ .../LottoTicketsAutoInputStrategy.java | 16 +++--- .../strategy/LottoTicketsInputStrategy.java | 37 ++++++++++++ .../LottoTicketsManuelInputStrategy.java | 56 +++++++++++++++++++ .../WinningLottoNumbersInputStrategy.java | 21 +++---- .../camp/lotto/domain/vo/LottoAmountTest.java | 2 +- .../camp/lotto/domain/vo/LottoCountTest.java | 47 ++++++++++++++++ 22 files changed, 379 insertions(+), 87 deletions(-) create mode 100644 src/main/java/com/nextstep/camp/lotto/domain/exception/LottoCountCannotBeNegativeException.java create mode 100644 src/main/java/com/nextstep/camp/lotto/domain/exception/LottoNumberInputRegexException.java create mode 100644 src/main/java/com/nextstep/camp/lotto/domain/vo/LottoCount.java create mode 100644 src/main/java/com/nextstep/camp/lotto/view/component/LottoManuelCountInput.java create mode 100644 src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsAutoInput.java create mode 100644 src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsManualInput.java create mode 100644 src/main/java/com/nextstep/camp/lotto/view/strategy/LottoManuelCountInputStrategy.java create mode 100644 src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsInputStrategy.java create mode 100644 src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsManuelInputStrategy.java create mode 100644 src/test/java/com/nextstep/camp/lotto/domain/vo/LottoCountTest.java diff --git a/src/main/java/com/nextstep/camp/lotto/domain/entity/LottoTickets.java b/src/main/java/com/nextstep/camp/lotto/domain/entity/LottoTickets.java index 9517d66994..9e66000d27 100644 --- a/src/main/java/com/nextstep/camp/lotto/domain/entity/LottoTickets.java +++ b/src/main/java/com/nextstep/camp/lotto/domain/entity/LottoTickets.java @@ -1,12 +1,13 @@ package com.nextstep.camp.lotto.domain.entity; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + import com.nextstep.camp.lotto.domain.exception.LottoTicketsCannotBeEmptyException; import com.nextstep.camp.lotto.domain.type.Rank; import com.nextstep.camp.lotto.domain.vo.WinningNumbers; -import java.util.List; -import java.util.stream.Collectors; - public class LottoTickets { private final List tickets; @@ -25,6 +26,13 @@ public static LottoTickets of(List tickets) { return new LottoTickets(tickets); } + public static LottoTickets of(LottoTickets manualTickets, LottoTickets autoTickets) { + List allTickets = new ArrayList<>(); + allTickets.addAll(manualTickets.getTickets()); + allTickets.addAll(autoTickets.getTickets()); + return new LottoTickets(allTickets); + } + public int size() { return tickets.size(); } diff --git a/src/main/java/com/nextstep/camp/lotto/domain/entity/WinningStatistics.java b/src/main/java/com/nextstep/camp/lotto/domain/entity/WinningStatistics.java index b3476eba2c..265bbb95ac 100644 --- a/src/main/java/com/nextstep/camp/lotto/domain/entity/WinningStatistics.java +++ b/src/main/java/com/nextstep/camp/lotto/domain/entity/WinningStatistics.java @@ -1,13 +1,11 @@ package com.nextstep.camp.lotto.domain.entity; -import com.nextstep.camp.lotto.domain.type.ProfitType; +import java.util.Map; + import com.nextstep.camp.lotto.domain.type.Rank; import com.nextstep.camp.lotto.domain.vo.LottoAmount; import com.nextstep.camp.lotto.domain.vo.RateOfReturn; -import java.util.Map; -import java.util.stream.Collectors; - public class WinningStatistics { private final Map resultCounts; private final LottoAmount spent; @@ -38,18 +36,4 @@ public Map getResultCounts() { public LottoAmount getSpent() { return spent; } - - @Override - public String toString() { - String resultString = resultCounts.entrySet().stream() - .map(entry -> entry.getKey() + " - " + entry.getValue() + "개") - .collect(Collectors.joining("\n")); - - RateOfReturn rateOfReturn = calculateRateOfReturn(spent); - ProfitType profitType = rateOfReturn.getProfitType(); - return resultString + "\n" + - "총 수익률은 " + - rateOfReturn.toString() + - "입니다.(기준이 1이기 때문에 결과적으로 " + profitType.getDescription() + "라는 의미임)"; - } } diff --git a/src/main/java/com/nextstep/camp/lotto/domain/exception/LottoCountCannotBeNegativeException.java b/src/main/java/com/nextstep/camp/lotto/domain/exception/LottoCountCannotBeNegativeException.java new file mode 100644 index 0000000000..6edeb91b40 --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/domain/exception/LottoCountCannotBeNegativeException.java @@ -0,0 +1,10 @@ +package com.nextstep.camp.lotto.domain.exception; + +public class LottoCountCannotBeNegativeException extends RuntimeException { + + private static final String MESSAGE = "로또 티켓 수는 0보다 작을 수 없습니다."; + + public LottoCountCannotBeNegativeException() { + super(MESSAGE); + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/domain/exception/LottoNumberInputRegexException.java b/src/main/java/com/nextstep/camp/lotto/domain/exception/LottoNumberInputRegexException.java new file mode 100644 index 0000000000..96e18a8cfa --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/domain/exception/LottoNumberInputRegexException.java @@ -0,0 +1,10 @@ +package com.nextstep.camp.lotto.domain.exception; + +public class LottoNumberInputRegexException extends IllegalArgumentException { + + private static final String MESSAGE = "로또 번호 입력 형식이 올바르지 않습니다. "; + + public LottoNumberInputRegexException() { + super(MESSAGE); + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/domain/strategy/LottoAutoPicker.java b/src/main/java/com/nextstep/camp/lotto/domain/strategy/LottoAutoPicker.java index e13e90c4ef..0c6f204d57 100644 --- a/src/main/java/com/nextstep/camp/lotto/domain/strategy/LottoAutoPicker.java +++ b/src/main/java/com/nextstep/camp/lotto/domain/strategy/LottoAutoPicker.java @@ -1,16 +1,16 @@ package com.nextstep.camp.lotto.domain.strategy; -import com.nextstep.camp.lotto.domain.entity.LottoTicket; -import com.nextstep.camp.lotto.domain.vo.LottoAmount; - import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import com.nextstep.camp.lotto.domain.entity.LottoTicket; +import com.nextstep.camp.lotto.domain.vo.LottoCount; + public class LottoAutoPicker implements LottoPicker { - private final LottoAmount amount; + private final LottoCount count; private static final int LOTTO_MIN = 1; private static final int LOTTO_MAX = 45; @@ -19,19 +19,19 @@ public class LottoAutoPicker implements LottoPicker { .boxed() .collect(Collectors.toList()); - private LottoAutoPicker(LottoAmount amount) { - this.amount = amount; + private LottoAutoPicker(LottoCount count) { + this.count = count; } - public static LottoAutoPicker of(LottoAmount amount) { + public static LottoAutoPicker of(LottoCount amount) { return new LottoAutoPicker(amount); } @Override public List pick() { - return IntStream.range(0, this.amount.lottoCount()) - .mapToObj(i -> LottoTicket.of(generateOne())) - .collect(Collectors.toList()); + return this.count.intStream() + .mapToObj(count -> LottoTicket.of(generateOne())) + .collect(Collectors.toList()); } private List generateOne() { diff --git a/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoAmount.java b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoAmount.java index 73c0f2fdb6..253d689598 100644 --- a/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoAmount.java +++ b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoAmount.java @@ -31,11 +31,11 @@ public static LottoAmount of(int value) { return new LottoAmount(value); } - public int lottoCount() { - return value / LOTTO_PRICE; - } - public int getValue() { return value; } + + public LottoCount getLottoCount() { + return LottoCount.of(value / LOTTO_PRICE); + } } diff --git a/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoCount.java b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoCount.java new file mode 100644 index 0000000000..8ae389dc4f --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoCount.java @@ -0,0 +1,50 @@ +package com.nextstep.camp.lotto.domain.vo; + +import java.util.stream.IntStream; + +import com.nextstep.camp.lotto.domain.exception.LottoCountCannotBeNegativeException; + +public class LottoCount { + private final int value; + + private LottoCount(int value) { + validate(value); + this.value = value; + } + + private static void validate(int value) { + if (value < 0) { + throw new LottoCountCannotBeNegativeException(); + } + } + + public static LottoCount of(int intValue) { + return new LottoCount(intValue); + } + + public int getCount() { + return value; + } + + public IntStream intStream() { + return IntStream.range(0, value); + } + + public LottoCount minus(LottoCount other) { + validateMinus(other); + return LottoCount.of(this.value - other.value); + } + + private void validateMinus(LottoCount other) { + if (this.value < other.value) { + throw new LottoCountCannotBeNegativeException(); + } + } + + @Override + public String toString() { + return "LottoCount{" + + "count=" + value + + '}'; + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumber.java b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumber.java index 6ccb53146e..d5e5a8ed95 100644 --- a/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumber.java +++ b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumber.java @@ -1,14 +1,14 @@ package com.nextstep.camp.lotto.domain.vo; -import com.nextstep.camp.lotto.domain.exception.LottoNumberOutOfRangeException; - import java.util.Objects; +import com.nextstep.camp.lotto.domain.exception.LottoNumberOutOfRangeException; + public class LottoNumber { private final int value; - private static final int MIN_VALUE = 1; - private static final int MAX_VALUE = 45; + public static final int MIN_VALUE = 1; + public static final int MAX_VALUE = 45; private LottoNumber(int value) { validate(value); diff --git a/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumbers.java b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumbers.java index ef8fe63a3b..9e0d181aa9 100644 --- a/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumbers.java +++ b/src/main/java/com/nextstep/camp/lotto/domain/vo/LottoNumbers.java @@ -1,17 +1,15 @@ package com.nextstep.camp.lotto.domain.vo; +import java.util.*; +import java.util.stream.Collectors; + import com.nextstep.camp.lotto.domain.exception.LottoNumberDuplicatedException; import com.nextstep.camp.lotto.domain.exception.LottoNumbersSizeException; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.stream.Collectors; - public class LottoNumbers { private final List numbers; - private static final int LOTTO_NUMBERS_SIZE = 6; + public static final int LOTTO_NUMBERS_SIZE = 6; private LottoNumbers(List rawNumbers) { validate(rawNumbers); diff --git a/src/main/java/com/nextstep/camp/lotto/view/LottoInputView.java b/src/main/java/com/nextstep/camp/lotto/view/LottoInputView.java index 090efe43be..039a07c125 100644 --- a/src/main/java/com/nextstep/camp/lotto/view/LottoInputView.java +++ b/src/main/java/com/nextstep/camp/lotto/view/LottoInputView.java @@ -1,16 +1,14 @@ package com.nextstep.camp.lotto.view; import com.nextstep.camp.lotto.domain.vo.LottoAmount; -import com.nextstep.camp.lotto.view.component.LottoAmountInput; -import com.nextstep.camp.lotto.view.component.LottoTicketsInput; -import com.nextstep.camp.lotto.view.component.WinningNumbersInput; +import com.nextstep.camp.lotto.domain.vo.LottoCount; +import com.nextstep.camp.lotto.view.component.*; import com.nextstep.camp.lotto.view.dto.LottoInputData; -import com.nextstep.camp.lotto.view.strategy.LottoAmountInputStrategy; -import com.nextstep.camp.lotto.view.strategy.LottoTicketsAutoInputStrategy; -import com.nextstep.camp.lotto.view.strategy.WinningNumbersInputStrategy; +import com.nextstep.camp.lotto.view.strategy.*; public class LottoInputView { private final LottoAmountInput lottoAmountInput; + private final LottoManuelCountInput lottoManuelCountInput; private final WinningNumbersInput winningNumbersInput; private LottoTicketsInput lottoTicketsInput; @@ -19,7 +17,9 @@ public class LottoInputView { private LottoInputView() { LottoAmountInput lottoAmountInput = LottoAmountInput.create(LottoAmountInputStrategy.ofSystemIn()); WinningNumbersInput winningNumbersInput = WinningNumbersInput.create(WinningNumbersInputStrategy.of()); + LottoManuelCountInput lottoManuelCountInput = LottoManuelCountInput.create(LottoManuelCountInputStrategy.ofSystemIn()); this.lottoAmountInput = lottoAmountInput; + this.lottoManuelCountInput = lottoManuelCountInput; this.winningNumbersInput = winningNumbersInput; } @@ -29,8 +29,9 @@ public static LottoInputView publish() { public void render() { LottoAmount lottoAmount = this.lottoAmountInput.action(); + LottoCount lottoManuelCount = this.lottoManuelCountInput.action(); - LottoTicketsAutoInputStrategy inputStrategy = LottoTicketsAutoInputStrategy.of(lottoAmount); + LottoTicketsInputStrategy inputStrategy = LottoTicketsInputStrategy.of(lottoAmount, lottoManuelCount); this.lottoTicketsInput = LottoTicketsInput.create(inputStrategy); this.lottoTicketsInput.action(); diff --git a/src/main/java/com/nextstep/camp/lotto/view/component/LottoManuelCountInput.java b/src/main/java/com/nextstep/camp/lotto/view/component/LottoManuelCountInput.java new file mode 100644 index 0000000000..15c75f97aa --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/view/component/LottoManuelCountInput.java @@ -0,0 +1,21 @@ +package com.nextstep.camp.lotto.view.component; + +import com.nextstep.camp.common.strategy.InputStrategy; +import com.nextstep.camp.common.view.component.AbstractInput; +import com.nextstep.camp.lotto.domain.vo.LottoCount; + +public class LottoManuelCountInput extends AbstractInput { + + private LottoManuelCountInput(InputStrategy inputStrategy) { + super(inputStrategy); + } + + public static LottoManuelCountInput create(InputStrategy inputStrategy) { + return new LottoManuelCountInput(inputStrategy); + } + + @Override + public String getLabel() { + return "\n수동으로 구매할 로또 수를 입력해 주세요."; + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsAutoInput.java b/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsAutoInput.java new file mode 100644 index 0000000000..d26d9181f7 --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsAutoInput.java @@ -0,0 +1,34 @@ +package com.nextstep.camp.lotto.view.component; + +import com.nextstep.camp.common.strategy.InputStrategy; +import com.nextstep.camp.common.view.component.AbstractInput; +import com.nextstep.camp.lotto.domain.entity.LottoTickets; +import com.nextstep.camp.lotto.domain.vo.LottoCount; + +public class LottoTicketsAutoInput extends AbstractInput { + + private final LottoCount autoCount; + private final LottoCount manualCount; + + private LottoTicketsAutoInput(InputStrategy inputStrategy, LottoCount autoCount, LottoCount manualCount) { + super(inputStrategy); + this.autoCount = autoCount; + this.manualCount = manualCount; + } + + public static LottoTicketsAutoInput create(InputStrategy inputStrategy, LottoCount autoCount, LottoCount manualCount) { + return new LottoTicketsAutoInput(inputStrategy, autoCount, manualCount); + } + + @Override + public String getLabel() { + return String.format("수동으로 %d장, 자동으로 %d개를 구매했습니다.", manualCount.getCount(), autoCount.getCount()); + } + + @Override + public LottoTickets action() { + LottoTickets lottoTickets = super.action(); + System.out.println(lottoTickets); + return lottoTickets; + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsInput.java b/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsInput.java index 6942b6dceb..df0b79c720 100644 --- a/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsInput.java +++ b/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsInput.java @@ -16,13 +16,6 @@ public static LottoTicketsInput create(InputStrategy inputStrategy @Override public String getLabel() { - return "14개를 구매했습니다."; - } - - @Override - public LottoTickets action() { - LottoTickets lottoTickets = super.action(); - System.out.println(lottoTickets); - return lottoTickets; + return ""; } } diff --git a/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsManualInput.java b/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsManualInput.java new file mode 100644 index 0000000000..05220dccd6 --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/view/component/LottoTicketsManualInput.java @@ -0,0 +1,21 @@ +package com.nextstep.camp.lotto.view.component; + +import com.nextstep.camp.common.strategy.InputStrategy; +import com.nextstep.camp.common.view.component.AbstractInput; +import com.nextstep.camp.lotto.domain.entity.LottoTickets; + +public class LottoTicketsManualInput extends AbstractInput { + + private LottoTicketsManualInput(InputStrategy inputStrategy) { + super(inputStrategy); + } + + public static LottoTicketsManualInput create(InputStrategy inputStrategy) { + return new LottoTicketsManualInput(inputStrategy); + } + + @Override + public String getLabel() { + return "\n수동으로 구매할 로또 번호를 입력해 주세요."; + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/view/strategy/BonusNumberInputStrategy.java b/src/main/java/com/nextstep/camp/lotto/view/strategy/BonusNumberInputStrategy.java index 36ac8903fc..9ed96e75a4 100644 --- a/src/main/java/com/nextstep/camp/lotto/view/strategy/BonusNumberInputStrategy.java +++ b/src/main/java/com/nextstep/camp/lotto/view/strategy/BonusNumberInputStrategy.java @@ -1,17 +1,15 @@ package com.nextstep.camp.lotto.view.strategy; +import java.util.Scanner; + import com.nextstep.camp.common.strategy.InputStrategy; import com.nextstep.camp.lotto.domain.exception.LottoNumberOutOfRangeException; import com.nextstep.camp.lotto.domain.vo.LottoNumber; -import java.util.Scanner; - public class BonusNumberInputStrategy implements InputStrategy { private final Scanner scanner; private static final String BONUS_NUMBER_REGEX = "^\\d{1,2}$";; - private static final int MIN_BONUS_NUMBER = 1; - private static final int MAX_BONUS_NUMBER = 45; private BonusNumberInputStrategy(Scanner scanner) { this.scanner = scanner; @@ -41,7 +39,7 @@ private void validate(String value) { } private void validateRange(int number) { - if (number < MIN_BONUS_NUMBER || number > MAX_BONUS_NUMBER) { + if (number < LottoNumber.MIN_VALUE || number > LottoNumber.MAX_VALUE) { throw new LottoNumberOutOfRangeException(); } } diff --git a/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoManuelCountInputStrategy.java b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoManuelCountInputStrategy.java new file mode 100644 index 0000000000..ddeb1428ce --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoManuelCountInputStrategy.java @@ -0,0 +1,29 @@ +package com.nextstep.camp.lotto.view.strategy; + +import java.util.Scanner; + +import com.nextstep.camp.common.strategy.InputStrategy; +import com.nextstep.camp.lotto.domain.vo.LottoCount; + +public class LottoManuelCountInputStrategy implements InputStrategy { + private final Scanner scanner; + + private LottoManuelCountInputStrategy(Scanner scanner) { + this.scanner = scanner; + } + + public static LottoManuelCountInputStrategy ofSystemIn() { + Scanner scanner = new Scanner(System.in); + return new LottoManuelCountInputStrategy(scanner); + } + + public static LottoManuelCountInputStrategy of(Scanner scanner) { + return new LottoManuelCountInputStrategy(scanner); + } + + @Override + public LottoCount read() { + int intValue = Integer.parseInt(scanner.nextLine()); + return LottoCount.of(intValue); + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsAutoInputStrategy.java b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsAutoInputStrategy.java index 9dbaae0051..3623a068e2 100644 --- a/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsAutoInputStrategy.java +++ b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsAutoInputStrategy.java @@ -1,28 +1,28 @@ package com.nextstep.camp.lotto.view.strategy; +import java.util.List; + import com.nextstep.camp.common.strategy.InputStrategy; import com.nextstep.camp.lotto.domain.entity.LottoTicket; import com.nextstep.camp.lotto.domain.entity.LottoTickets; import com.nextstep.camp.lotto.domain.strategy.LottoAutoPicker; -import com.nextstep.camp.lotto.domain.vo.LottoAmount; - -import java.util.List; +import com.nextstep.camp.lotto.domain.vo.LottoCount; public class LottoTicketsAutoInputStrategy implements InputStrategy { - private final LottoAmount lottoAmount; + private final LottoCount lottoCount; - private LottoTicketsAutoInputStrategy(LottoAmount lottoAmount) { - this.lottoAmount = lottoAmount; + private LottoTicketsAutoInputStrategy(LottoCount lottoCount) { + this.lottoCount = lottoCount; } - public static LottoTicketsAutoInputStrategy of(LottoAmount lottoAmount) { + public static LottoTicketsAutoInputStrategy of(LottoCount lottoAmount) { return new LottoTicketsAutoInputStrategy(lottoAmount); } @Override public LottoTickets read() { - List lottoTickets = LottoAutoPicker.of(lottoAmount).pick(); + List lottoTickets = LottoAutoPicker.of(lottoCount).pick(); return LottoTickets.of(lottoTickets); } } diff --git a/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsInputStrategy.java b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsInputStrategy.java new file mode 100644 index 0000000000..56a4444bbf --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsInputStrategy.java @@ -0,0 +1,37 @@ +package com.nextstep.camp.lotto.view.strategy; + +import com.nextstep.camp.common.strategy.InputStrategy; +import com.nextstep.camp.lotto.domain.entity.LottoTickets; +import com.nextstep.camp.lotto.domain.vo.LottoAmount; +import com.nextstep.camp.lotto.domain.vo.LottoCount; +import com.nextstep.camp.lotto.view.component.LottoTicketsAutoInput; +import com.nextstep.camp.lotto.view.component.LottoTicketsManualInput; + +public class LottoTicketsInputStrategy implements InputStrategy { + private final LottoAmount lottoAmount; + private final LottoCount lottoManuelCount; + + private LottoTicketsInputStrategy(LottoAmount lottoAmount, LottoCount lottoManuelCount) { + this.lottoAmount = lottoAmount; + this.lottoManuelCount = lottoManuelCount; + } + + public static LottoTicketsInputStrategy of(LottoAmount lottoAmount, LottoCount lottoCount) { + return new LottoTicketsInputStrategy(lottoAmount, lottoCount); + } + + @Override + public LottoTickets read() { + LottoTicketsManuelInputStrategy lottoTicketsManuelInputStrategy = LottoTicketsManuelInputStrategy.ofSystemIn(lottoManuelCount); + LottoTicketsManualInput lottoTicketsManualInput = LottoTicketsManualInput.create(lottoTicketsManuelInputStrategy); + LottoTickets manualLottoTickets = lottoTicketsManualInput.action(); + + LottoCount lottoAutoCount = lottoAmount.getLottoCount().minus(lottoManuelCount); + + LottoTicketsAutoInputStrategy lottoTicketsAutoInputStrategy = LottoTicketsAutoInputStrategy.of(lottoAutoCount); + LottoTicketsAutoInput lottoTicketsAutoInput = LottoTicketsAutoInput.create(lottoTicketsAutoInputStrategy, lottoAutoCount, lottoManuelCount); + LottoTickets autoLottoTickets = lottoTicketsAutoInput.action(); + + return LottoTickets.of(manualLottoTickets, autoLottoTickets); + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsManuelInputStrategy.java b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsManuelInputStrategy.java new file mode 100644 index 0000000000..de6cccefc0 --- /dev/null +++ b/src/main/java/com/nextstep/camp/lotto/view/strategy/LottoTicketsManuelInputStrategy.java @@ -0,0 +1,56 @@ +package com.nextstep.camp.lotto.view.strategy; + +import java.util.*; +import java.util.stream.Collectors; + +import com.nextstep.camp.common.strategy.InputStrategy; +import com.nextstep.camp.lotto.domain.entity.LottoTicket; +import com.nextstep.camp.lotto.domain.entity.LottoTickets; +import com.nextstep.camp.lotto.domain.exception.LottoNumberInputRegexException; +import com.nextstep.camp.lotto.domain.vo.LottoCount; + +public class LottoTicketsManuelInputStrategy implements InputStrategy { + private final Scanner scanner; + private final LottoCount lottoCount; + + private static final String LOTTO_NUMBER_REGEX = "^\\s*\\d{1,2}\\s*(,\\s*\\d{1,2}\\s*){5}$";; + private static final String DELIMITER = ",\\s?"; + + private LottoTicketsManuelInputStrategy(Scanner scanner, LottoCount lottoCount) { + this.scanner = scanner; + this.lottoCount = lottoCount; + } + + public static LottoTicketsManuelInputStrategy ofSystemIn(LottoCount lottoCount) { + Scanner scanner = new Scanner(System.in); + return new LottoTicketsManuelInputStrategy(scanner, lottoCount); + } + + @Override + public LottoTickets read() { + List manualTickets = lottoCount.intStream() + .mapToObj(count -> getLottoTicket()) + .collect(Collectors.toList()); + + return LottoTickets.of(manualTickets); + } + + private LottoTicket getLottoTicket() { + String line = scanner.nextLine(); + validate(line); + List numbers = parseToNumbers(line); + return LottoTicket.of(numbers); + } + + private static void validate(String line) { + if (!line.matches(LOTTO_NUMBER_REGEX)) { + throw new LottoNumberInputRegexException(); + } + } + + private List parseToNumbers(String input) { + return Arrays.stream(input.split(DELIMITER)) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/nextstep/camp/lotto/view/strategy/WinningLottoNumbersInputStrategy.java b/src/main/java/com/nextstep/camp/lotto/view/strategy/WinningLottoNumbersInputStrategy.java index a0108714fe..1941b1817c 100644 --- a/src/main/java/com/nextstep/camp/lotto/view/strategy/WinningLottoNumbersInputStrategy.java +++ b/src/main/java/com/nextstep/camp/lotto/view/strategy/WinningLottoNumbersInputStrategy.java @@ -1,23 +1,18 @@ package com.nextstep.camp.lotto.view.strategy; +import java.util.*; +import java.util.stream.Collectors; + import com.nextstep.camp.common.strategy.InputStrategy; -import com.nextstep.camp.lotto.domain.exception.LottoNumberOutOfRangeException; -import com.nextstep.camp.lotto.domain.exception.LottoNumbersSizeException; +import com.nextstep.camp.lotto.domain.exception.*; +import com.nextstep.camp.lotto.domain.vo.LottoNumber; import com.nextstep.camp.lotto.domain.vo.LottoNumbers; -import java.util.Arrays; -import java.util.List; -import java.util.Scanner; -import java.util.stream.Collectors; - public class WinningLottoNumbersInputStrategy implements InputStrategy { private final Scanner scanner; private static final String WINNING_NUMBER_REGEX = "^\\s*\\d{1,2}\\s*(,\\s*\\d{1,2}\\s*){5}$";; private static final String DELIMITER = ",\\s?"; - private static final int WINNING_NUMBER_SIZE = 6; - private static final int MIN_WINNING_NUMBER = 1; - private static final int MAX_WINNING_NUMBER = 45; private WinningLottoNumbersInputStrategy(Scanner scanner) { this.scanner = scanner; @@ -43,12 +38,12 @@ public LottoNumbers read() { private static void validate(String value) { if (!value.matches(WINNING_NUMBER_REGEX)) { - throw new LottoNumberOutOfRangeException(); + throw new LottoNumberInputRegexException(); } } private static void validate(List numbers) { - if (numbers.size() != WINNING_NUMBER_SIZE) { + if (numbers.size() != LottoNumbers.LOTTO_NUMBERS_SIZE) { throw new LottoNumbersSizeException(); } if (isOutOfRange(numbers)) { @@ -57,7 +52,7 @@ private static void validate(List numbers) { } private static boolean isOutOfRange(List numbers) { - return numbers.stream().anyMatch(n -> n < MIN_WINNING_NUMBER || n > MAX_WINNING_NUMBER); + return numbers.stream().anyMatch(n -> n < LottoNumber.MIN_VALUE || n > LottoNumber.MAX_VALUE); } private List splitWinningNumbersString(String value) { diff --git a/src/test/java/com/nextstep/camp/lotto/domain/vo/LottoAmountTest.java b/src/test/java/com/nextstep/camp/lotto/domain/vo/LottoAmountTest.java index 627bfc746c..f63d57991f 100644 --- a/src/test/java/com/nextstep/camp/lotto/domain/vo/LottoAmountTest.java +++ b/src/test/java/com/nextstep/camp/lotto/domain/vo/LottoAmountTest.java @@ -28,7 +28,7 @@ static Stream validAmounts() { void valid_amount_creates_LottoAmount(int input, int expectedCount) { LottoAmount amount = LottoAmount.of(input); assertEquals(input, amount.getValue()); - assertEquals(expectedCount, amount.lottoCount()); + assertEquals(expectedCount, amount.getLottoCount().getCount()); } static Stream zeroOrNegativeAmounts() { diff --git a/src/test/java/com/nextstep/camp/lotto/domain/vo/LottoCountTest.java b/src/test/java/com/nextstep/camp/lotto/domain/vo/LottoCountTest.java new file mode 100644 index 0000000000..7411f0f008 --- /dev/null +++ b/src/test/java/com/nextstep/camp/lotto/domain/vo/LottoCountTest.java @@ -0,0 +1,47 @@ +package com.nextstep.camp.lotto.domain.vo; + +import com.nextstep.camp.lotto.domain.exception.LottoCountCannotBeNegativeException; +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; + +class LottoCountTest { + + @ParameterizedTest(name = "input={0}") + @ValueSource(ints = {0, 1, 5, 100}) + @DisplayName("0 이상 정수로 LottoCount를 생성할 수 있다") + void validLottoCountCreation(int input) { + LottoCount count = LottoCount.of(input); + assertEquals(input, count.getCount()); + } + + @ParameterizedTest(name = "input={0}") + @ValueSource(ints = {-1, -10, -999}) + @DisplayName("음수로 LottoCount를 생성하면 예외가 발생한다") + void invalidLottoCountCreation(int input) { + assertThrows(LottoCountCannotBeNegativeException.class, () -> LottoCount.of(input)); + } + + @Test + @DisplayName("LottoCount.minus로 정상적으로 값을 뺄 수 있다") + void minusShouldSubtractProperly() { + LottoCount base = LottoCount.of(5); + LottoCount subtract = LottoCount.of(2); + + LottoCount result = base.minus(subtract); + assertEquals(3, result.getCount()); + } + + @Test + @DisplayName("LottoCount.minus로 음수가 되면 예외 발생") + void minusShouldThrowIfNegative() { + LottoCount base = LottoCount.of(2); + LottoCount subtract = LottoCount.of(5); + + assertThrows(LottoCountCannotBeNegativeException.class, () -> base.minus(subtract)); + } +}