diff --git a/README.md b/README.md index 4487152817..cda8c4aa42 100644 --- a/README.md +++ b/README.md @@ -81,4 +81,24 @@ - indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. - 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. - 자바 코드 컨벤션을 지키면서 프로그래밍한다. -- else 예약어를 쓰지 않는다. \ No newline at end of file +- else 예약어를 쓰지 않는다. + +## Step4. 로또(수동) + +### 기능 요구사항 + +- [ ] 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다. +- [ ] 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다. + +### 프로그래밍 요구사항 + +- 규칙 3: 모든 원시값과 문자열을 포장한다. +- 규칙 5: 줄여쓰지 않는다(축약 금지). +- 예외 처리를 통해 에러가 발생하지 않도록 한다. +- 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 +- java enum을 적용해 프로그래밍을 구현한다. +- 규칙 8: 일급 콜렉션을 쓴다. +- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. +- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. +- 자바 코드 컨벤션을 지키면서 프로그래밍한다. +- else 예약어를 쓰지 않는다. diff --git a/src/main/java/lotto/Main.java b/src/main/java/lotto/Main.java index 073e6b4a37..fd27eeb46d 100644 --- a/src/main/java/lotto/Main.java +++ b/src/main/java/lotto/Main.java @@ -1,9 +1,7 @@ package lotto; - import lotto.domain.*; - import static lotto.domain.LottoResult.*; import static lotto.view.InputView.*; import static lotto.view.InputView.inputBuyPrice; @@ -13,11 +11,24 @@ public class Main { public static void main(String[] args) { printStart(); - LottoPrice lottoPrice = inputBuyPrice(); + int price = inputBuyPrice(); + + printPassiveBuyLottoCount(); + int passiveCount = inputPassiveBuyLottoCount(); + LottoPrice lottoPrice = new LottoPrice(price, passiveCount); + + PurchasedLottos purchasedLottos = new PurchasedLottos(); + if (passiveCount > 0) { + printPassiveBuyLotto(); + inputPassiveBuyLotto(purchasedLottos, passiveCount); + } + printLottoCount(lottoPrice); - BuyLotto buyLotto = LottoMachine.createLotto(lottoPrice); - printBuyLotto(buyLotto); + PurchasedLottos createLottos = LottoMachine.createLotto(lottoPrice); + purchasedLottos.addAll(createLottos); + + printBuyLotto(purchasedLottos); printResultLottoNumber(); Lotto resultLotto = inputResultLottoNumber(); @@ -26,7 +37,7 @@ public static void main(String[] args) { WinningLotto winningLotto = new WinningLotto(resultLotto, bonusNumber); printResultMessage(); - LottoResult lottoResult = winningLotto.checkLottoNumber(buyLotto); + LottoResult lottoResult = winningLotto.checkLottoNumber(purchasedLottos); printResult(lottoResult); printProfit(profitPercent(lottoPrice)); diff --git a/src/main/java/lotto/domain/Lotto.java b/src/main/java/lotto/domain/Lotto.java index f1a8d7f621..900f9447da 100644 --- a/src/main/java/lotto/domain/Lotto.java +++ b/src/main/java/lotto/domain/Lotto.java @@ -8,7 +8,9 @@ public class Lotto { private final Set numbers; public Lotto(Integer... numbers) { - this(Arrays.stream(numbers).map(NumberElement::create).collect(Collectors.toSet())); + this(Arrays.stream(numbers) + .map(NumberElement::create) + .collect(Collectors.toCollection(LinkedHashSet::new))); } public Lotto(Set numbers) { diff --git a/src/main/java/lotto/domain/LottoMachine.java b/src/main/java/lotto/domain/LottoMachine.java index ed807f87e2..876d3fe1e9 100644 --- a/src/main/java/lotto/domain/LottoMachine.java +++ b/src/main/java/lotto/domain/LottoMachine.java @@ -4,29 +4,27 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; public class LottoMachine { + public static final int MIN_LOTTO_NUMBER = 1; + public static final int MAX_LOTTO_NUMBER = 45; + private static final List lottoNumbers; static { lottoNumbers = new ArrayList<>(); - for (int i = 1; i <= 45; i++) { + for (int i = MIN_LOTTO_NUMBER; i <= MAX_LOTTO_NUMBER; i++) { lottoNumbers.add(NumberElement.create(i)); } } - public static BuyLotto createLotto(LottoPrice lottoPrice) { - List lottoNumbers = new ArrayList<>(); - + public static PurchasedLottos createLotto(LottoPrice lottoPrice) { + List lottos = new ArrayList<>(); lottoPrice.create(() -> { Collections.shuffle(LottoMachine.lottoNumbers); - lottoNumbers.add(new Lotto(new HashSet<>( - LottoMachine.lottoNumbers.subList(0, 6).stream().collect(Collectors.toSet()))) - ); + lottos.add(new Lotto(new HashSet<>(LottoMachine.lottoNumbers.subList(0, 6)))); }); - - return new BuyLotto(lottoNumbers); + return new PurchasedLottos(lottos); } } diff --git a/src/main/java/lotto/domain/LottoPrice.java b/src/main/java/lotto/domain/LottoPrice.java index 66a9d7e812..c330885bcb 100644 --- a/src/main/java/lotto/domain/LottoPrice.java +++ b/src/main/java/lotto/domain/LottoPrice.java @@ -7,12 +7,12 @@ public class LottoPrice { private final Price price; private final TicketCount ticketCount; - public LottoPrice(int price) { - this(price, price / 1_000); + public LottoPrice(int price, int passiveTicketCount) { + this(price, new TicketCount(price, passiveTicketCount)); } - public LottoPrice(int price, int ticketCount) { - this(new Price(price), new TicketCount(ticketCount)); + public LottoPrice(int price, TicketCount ticketCount) { + this(new Price(price), ticketCount); } public LottoPrice(Price price, TicketCount ticketCount) { @@ -21,17 +21,15 @@ public LottoPrice(Price price, TicketCount ticketCount) { } public void create(Runnable runnable) { - for (int i = 0; i < this.ticketCount.getValue(); i++) { - runnable.run(); - } + this.ticketCount.create(runnable); } public double profitPercent(int totalResultPrice) { return price.profitPercent(totalResultPrice); } - public int getTicketCount() { - return ticketCount.getValue(); + public TicketCount getTicketCount() { + return ticketCount; } @Override diff --git a/src/main/java/lotto/domain/NumberElement.java b/src/main/java/lotto/domain/NumberElement.java index d48c99b3a3..dc1c3a584d 100644 --- a/src/main/java/lotto/domain/NumberElement.java +++ b/src/main/java/lotto/domain/NumberElement.java @@ -6,20 +6,22 @@ public class NumberElement { private static final Map numberElementMap = new HashMap<>(); + private static final int START_NUMBER = 1; + private static final int END_NUMBER = 45; static { - for (int i = 1; i <= 45; i++) { + for (int i = START_NUMBER; i <= END_NUMBER; i++) { numberElementMap.put(i, new NumberElement(i)); } } private final int value; - public NumberElement() { + private NumberElement() { this(0); } - public NumberElement(int value) { + private NumberElement(int value) { validationRange(value); this.value = value; } @@ -36,7 +38,7 @@ private static void validationRange(int value) { } private static boolean isRange(int value) { - return 1 > value || value > 45; + return START_NUMBER > value || value > END_NUMBER; } @Override diff --git a/src/main/java/lotto/domain/Price.java b/src/main/java/lotto/domain/Price.java index 4c420a643e..4c25b189ff 100644 --- a/src/main/java/lotto/domain/Price.java +++ b/src/main/java/lotto/domain/Price.java @@ -31,11 +31,15 @@ private void checkMinPrice(int value) { } private void checkDivisibleByThousand(int value) { - if (value % 1000 != 0) { + if (value % 1_000 != 0) { throw new IllegalArgumentException("1000 단위로 입력해주세요."); } } + public int autoTiketCount(int passiveTicketCount) { + return (this.value / 1_000) - passiveTicketCount; + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; @@ -47,4 +51,5 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hashCode(getValue()); } + } diff --git a/src/main/java/lotto/domain/BuyLotto.java b/src/main/java/lotto/domain/PurchasedLottos.java similarity index 57% rename from src/main/java/lotto/domain/BuyLotto.java rename to src/main/java/lotto/domain/PurchasedLottos.java index 4a5d443e92..eb31a870a5 100644 --- a/src/main/java/lotto/domain/BuyLotto.java +++ b/src/main/java/lotto/domain/PurchasedLottos.java @@ -1,17 +1,22 @@ package lotto.domain; +import java.util.ArrayList; import java.util.List; import java.util.Objects; -public class BuyLotto { +public class PurchasedLottos { private final List lottos; - public BuyLotto(Integer... numbers) { + public PurchasedLottos() { + lottos = new ArrayList<>(); + } + + public PurchasedLottos(Integer... numbers) { this(List.of(new Lotto(numbers))); } - public BuyLotto(List lottos) { + public PurchasedLottos(List lottos) { this.lottos = lottos; } @@ -29,9 +34,25 @@ public LottoResult match(WinningLotto winningLotto) { return lottoResult; } + public void add(Lotto lotto){ + this.lottos.add(lotto); + } + + public void addAll(PurchasedLottos lottos){ + this.lottos.addAll(lottos.lottos); + } + + public String toLottosString() { + StringBuilder sb = new StringBuilder(); + for (Lotto lotto : lottos) { + sb.append(lotto).append("\n"); + } + return sb.toString(); + } + @Override public String toString() { - return "BuyLotto{" + + return "PurchasedLottos{" + "lotto=" + lottos + '}'; } @@ -39,7 +60,7 @@ public String toString() { @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - BuyLotto buyLotto = (BuyLotto) o; + PurchasedLottos buyLotto = (PurchasedLottos) o; return Objects.equals(getLottos(), buyLotto.getLottos()); } diff --git a/src/main/java/lotto/domain/TicketCount.java b/src/main/java/lotto/domain/TicketCount.java index 5e2f69643b..30e3b82a9e 100644 --- a/src/main/java/lotto/domain/TicketCount.java +++ b/src/main/java/lotto/domain/TicketCount.java @@ -4,25 +4,37 @@ public class TicketCount { - private final int value; + private final int autoTicketCount; + private final int passiveTicketCount; - public TicketCount(int value) { - this.value = value; + public TicketCount(int price, int passiveTicketCount) { + this.autoTicketCount = new Price(price).autoTiketCount(passiveTicketCount); + this.passiveTicketCount = passiveTicketCount; } - public int getValue() { - return this.value; + public void create(Runnable runnable) { + for (int i = 0; i < this.autoTicketCount; i++) { + runnable.run(); + } + } + + public int getAutoTicketCount() { + return this.autoTicketCount; + } + + public int getPassiveTicketCount() { + return passiveTicketCount; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; TicketCount that = (TicketCount) o; - return getValue() == that.getValue(); + return getAutoTicketCount() == that.getAutoTicketCount(); } @Override public int hashCode() { - return Objects.hashCode(getValue()); + return Objects.hashCode(getAutoTicketCount()); } } diff --git a/src/main/java/lotto/domain/WinningLotto.java b/src/main/java/lotto/domain/WinningLotto.java index 341d21447f..b3a8db4532 100644 --- a/src/main/java/lotto/domain/WinningLotto.java +++ b/src/main/java/lotto/domain/WinningLotto.java @@ -11,10 +11,6 @@ public class WinningLotto { private final Lotto numbers; private final NumberElement bonusNumber; - public WinningLotto(Integer... numbers) { - this(integerToLottoNumbers(numbers), NumberElement.create(0)); - } - public WinningLotto(List numbers, int bonusNumber) { this(integerToLottoNumbers(numbers), NumberElement.create(bonusNumber)); } @@ -29,14 +25,8 @@ public WinningLotto(Lotto numbers, NumberElement bonusNumber) { this.bonusNumber = bonusNumber; } - public LottoResult checkLottoNumber(BuyLotto buyLottos) { - return buyLottos.match(this); - } - - private static Lotto integerToLottoNumbers(Integer... numbers) { - return new Lotto(Arrays.stream(numbers) - .map(NumberElement::create) - .collect(Collectors.toSet())); + public LottoResult checkLottoNumber(PurchasedLottos purchasedLottos) { + return purchasedLottos.match(this); } private static Lotto integerToLottoNumbers(List numbers) { @@ -59,8 +49,8 @@ private static boolean isBonus(int count) { return count == 5; } - public LottoRank checkBonusNumber(Lotto buyLotto, NumberElement bonusNumber) { - if (buyLotto.checkBonusNumber(bonusNumber)) { + public LottoRank checkBonusNumber(Lotto purchasedLotto, NumberElement bonusNumber) { + if (purchasedLotto.checkBonusNumber(bonusNumber)) { return LottoRank.SECOND; } return LottoRank.THIRD; diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java index 1fa1d92599..932e8b8604 100644 --- a/src/main/java/lotto/view/InputView.java +++ b/src/main/java/lotto/view/InputView.java @@ -1,6 +1,6 @@ package lotto.view; -import lotto.domain.BuyLotto; +import lotto.domain.PurchasedLottos; import lotto.domain.Lotto; import lotto.domain.LottoPrice; @@ -12,24 +12,49 @@ public class InputView { private static final Scanner scanner = new Scanner(System.in); private static final String START_MESSAGE = "구입금액을 입력해 주세요."; - private static final String LOTTO_COUNT_MESSAGE = "%d개를 구매했습니다."; + private static final String LOTTO_COUNT_MESSAGE = "수동으로 %d장, 자동으로 %d개를 구매했습니다."; private static final String RESULT_LOTTO_MESSAGE = "지난 주 당첨 번호를 입력해 주세요."; private static final String BONUS_NUMBER_MESSAGE = "보너스 볼을 입력해주세요."; + private static final String PASSIVE_BUY_COUNT_MESSAGE = "수동으로 구매할 로또 수를 입력해 주세요."; + private static final String PASSIVE_BUY_MESSAGE = "수동으로 구매할 번호를 입력해 주세요."; public static void printStart() { System.out.println(START_MESSAGE); } - public static LottoPrice inputBuyPrice() { - return new LottoPrice(scanner.nextInt()); + public static int inputBuyPrice() { + return scanner.nextInt(); } public static void printLottoCount(LottoPrice lottoPrice) { - System.out.println(String.format(LOTTO_COUNT_MESSAGE, lottoPrice.getTicketCount())); + System.out.println(String.format( + LOTTO_COUNT_MESSAGE, + lottoPrice.getTicketCount().getPassiveTicketCount(), + lottoPrice.getTicketCount().getAutoTicketCount()) + ); + } + + public static void printBuyLotto(PurchasedLottos purchasedLottos) { + System.out.println(purchasedLottos.toLottosString()); } - public static void printBuyLotto(BuyLotto buyLotto) { - buyLotto.getLottos().stream().forEach(System.out::println); + public static void printPassiveBuyLottoCount() { + System.out.println(PASSIVE_BUY_COUNT_MESSAGE); + } + + public static void printPassiveBuyLotto() { + System.out.println(PASSIVE_BUY_MESSAGE); + } + + public static void inputPassiveBuyLotto(PurchasedLottos purchasedLottos, int count) { + scanner.nextLine(); + for (int i = 0; i < count; i++) { + purchasedLottos.add(new Lotto(StringToIntegerArray(scanner.next()))); + } + } + + public static int inputPassiveBuyLottoCount() { + return scanner.nextInt(); } public static void printResultLottoNumber() { diff --git a/src/test/java/lotto/domain/LottoMachineTest.java b/src/test/java/lotto/domain/LottoMachineTest.java index 0b2a1b0cf7..721654ad45 100644 --- a/src/test/java/lotto/domain/LottoMachineTest.java +++ b/src/test/java/lotto/domain/LottoMachineTest.java @@ -2,13 +2,23 @@ import org.junit.jupiter.api.Test; +import java.util.List; + import static org.assertj.core.api.Assertions.*; public class LottoMachineTest { @Test void 로또번호_생성() { - BuyLotto results = LottoMachine.createLotto(new LottoPrice(5000)); - assertThat(results.getLottos()).hasSize(5); + + PurchasedLottos purchasedLottos = LottoMachine.createLotto(new LottoPrice(5000, 0)); + assertThat(purchasedLottos.getLottos()).hasSize(5); + } + + @Test + void 로또번호_수동_생성() { + PurchasedLottos purchasedLottos = LottoMachine.createLotto(new LottoPrice(5000, 1)); + purchasedLottos.addAll(new PurchasedLottos(List.of(new Lotto(1, 2, 3, 4, 5, 6)))); + assertThat(purchasedLottos.getLottos()).hasSize(5); } } diff --git a/src/test/java/lotto/domain/LottoPriceTest.java b/src/test/java/lotto/domain/LottoPriceTest.java index 73ea51aab7..b1fc6612dc 100644 --- a/src/test/java/lotto/domain/LottoPriceTest.java +++ b/src/test/java/lotto/domain/LottoPriceTest.java @@ -8,19 +8,25 @@ class LottoPriceTest { @Test - void 생성(){ - LottoPrice lottoPrice = new LottoPrice(100000); - assertThat(lottoPrice).isEqualTo(new LottoPrice(100000, 100)); + void 생성() { + LottoPrice lottoPrice = new LottoPrice(100000, 0); + assertThat(lottoPrice).isEqualTo(new LottoPrice(100000, 0)); } @Test - void 천원_이하_생성시_오류(){ - assertThatIllegalArgumentException().isThrownBy(() -> new LottoPrice(900)); + void 천원_이하_생성시_오류() { + assertThatIllegalArgumentException().isThrownBy(() -> new LottoPrice(900, 0)); } @Test - void 천원단위가_아닐경우_오류(){ - assertThatIllegalArgumentException().isThrownBy(() -> new LottoPrice(13340)); + void 천원단위가_아닐경우_오류() { + assertThatIllegalArgumentException().isThrownBy(() -> new LottoPrice(13340, 0)); + } + + @Test + void 수동_로또_생성() { + LottoPrice lottoPrice = new LottoPrice(10000, 3); + assertThat(lottoPrice).isEqualTo(new LottoPrice(10000, 3)); } } \ No newline at end of file diff --git a/src/test/java/lotto/domain/LottoResultTest.java b/src/test/java/lotto/domain/LottoResultTest.java index c92a37880f..b98c521477 100644 --- a/src/test/java/lotto/domain/LottoResultTest.java +++ b/src/test/java/lotto/domain/LottoResultTest.java @@ -15,7 +15,7 @@ void init() { @Test void 수익률_100퍼센트_미만() { - double result = LottoResult.profitPercent(new LottoPrice(100000)); + double result = LottoResult.profitPercent(new LottoPrice(100000, 0)); Assertions.assertThat(result).isEqualTo(0.5); } diff --git a/src/test/java/lotto/domain/WinningLottoTest.java b/src/test/java/lotto/domain/WinningLottoTest.java index ea1498b779..33efef4235 100644 --- a/src/test/java/lotto/domain/WinningLottoTest.java +++ b/src/test/java/lotto/domain/WinningLottoTest.java @@ -14,20 +14,20 @@ class WinningLottoTest { void 로또번호_3개_일치() { WinningLotto lottoNumber = new WinningLotto(List.of(1, 2, 3, 4, 5, 6), 30); LottoResult lottoResult = lottoNumber.checkLottoNumber( - new BuyLotto(13, 2, 33, 40, 1, 3)); + new PurchasedLottos(13, 2, 33, 40, 1, 3)); assertThat(lottoResult.getLottoNumberResult().getFirst()).isEqualTo(new LottoNumberResult(LottoRank.FIFTH, 1)); } @Test void 지난주_당첨번호_6개_미만() { assertThatIllegalArgumentException() - .isThrownBy(() -> new WinningLotto(1, 2, 3, 4, 5)); + .isThrownBy(() -> new WinningLotto(new Lotto(1, 2, 3, 4, 5), NumberElement.create(10))); } @Test void 로또번호_5개_보너스번호_일치() { WinningLotto lottoNumber = new WinningLotto(List.of(1, 2, 3, 4, 5, 6), 7); - LottoResult lottoResult = lottoNumber.checkLottoNumber(new BuyLotto(1, 2, 3, 4, 5, 7)); + LottoResult lottoResult = lottoNumber.checkLottoNumber(new PurchasedLottos(1, 2, 3, 4, 5, 7)); // [FOURTH(0), THIRD(0), SECOND(0), BONUS(1), FIRST(0)] assertThat(lottoResult.getLottoNumberResult().get(3)).isEqualTo(new LottoNumberResult(LottoRank.SECOND, 1)); }