diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index 31f5b45df5..f547b4b87c 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,5 +1,11 @@ package lotto; +import lotto.application.LottoMachine; +import lotto.domain.generator.ShuffleNumberGenerator; +import lotto.ui.InputView; +import lotto.ui.LottoController; +import lotto.ui.ResultView; + public class Application { public static void main(String[] args) { LottoMachine machine = new LottoMachine(new ShuffleNumberGenerator()); diff --git a/src/main/java/lotto/InputView.java b/src/main/java/lotto/InputView.java deleted file mode 100644 index ac8db7d376..0000000000 --- a/src/main/java/lotto/InputView.java +++ /dev/null @@ -1,28 +0,0 @@ -package lotto; - -import java.util.Arrays; -import java.util.List; -import java.util.Scanner; -import java.util.stream.Collectors; - -public class InputView { - private final Scanner scanner = new Scanner(System.in); - - public int readPurchaseAmount() { - System.out.println("구입금액을 입력해 주세요."); - return Integer.parseInt(scanner.nextLine().trim()); - } - - public List readWinningNumbers() { - System.out.println("지난 주 당첨 번호를 입력해 주세요."); - return Arrays.stream(scanner.nextLine().split(",")) - .map(String::trim) - .map(Integer::parseInt) - .collect(Collectors.toList()); - } - - public int readBonusNumber() { - System.out.println("보너스 볼을 입력해 주세요."); - return Integer.parseInt(scanner.nextLine().trim()); - } -} diff --git a/src/main/java/lotto/LottoController.java b/src/main/java/lotto/LottoController.java deleted file mode 100644 index d90aa4cec7..0000000000 --- a/src/main/java/lotto/LottoController.java +++ /dev/null @@ -1,29 +0,0 @@ -package lotto; - - -import java.util.List; - -public class LottoController { - private final InputView inputView; - private final ResultView resultView; - private final LottoMachine lottoMachine; - - public LottoController(InputView inputView, ResultView resultView, LottoMachine lottoMachine) { - this.inputView = inputView; - this.resultView = resultView; - this.lottoMachine = lottoMachine; - } - - public void run() { - Money money = Money.of(inputView.readPurchaseAmount()); - - Lottos tickets = lottoMachine.issue(money); - resultView.printLottos(tickets); - - Lotto winning = new Lotto(inputView.readWinningNumbers()); - WinningNumbers winningNumbers = new WinningNumbers(winning, inputView.readBonusNumber()); - - WinningStatistics stats = winningNumbers.match(tickets); - resultView.printStatistics(stats, money); - } -} diff --git a/src/main/java/lotto/LottoMachine.java b/src/main/java/lotto/LottoMachine.java deleted file mode 100644 index e8becac43a..0000000000 --- a/src/main/java/lotto/LottoMachine.java +++ /dev/null @@ -1,20 +0,0 @@ -package lotto; - -import java.util.ArrayList; -import java.util.List; - -public class LottoMachine { - private final LottoNumberGenerator generator; - - public LottoMachine(LottoNumberGenerator generator) { - this.generator = generator; - } - - public Lottos issue(Money money) { - List lottos = new ArrayList<>(); - for (int i = 0; i < money.ticketCount(); i++) { - lottos.add(new Lotto(generator.generate())); - } - return new Lottos(lottos); - } -} diff --git a/src/main/java/lotto/ResultView.java b/src/main/java/lotto/ResultView.java deleted file mode 100644 index 6f14fb820d..0000000000 --- a/src/main/java/lotto/ResultView.java +++ /dev/null @@ -1,28 +0,0 @@ -package lotto; - -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -public class ResultView { - public void printLottos(Lottos lottos) { - System.out.println(lottos.size() + "개를 구매했습니다."); - lottos.values().forEach(lotto -> System.out.println(lotto.numbers())); - } - - public void printStatistics(WinningStatistics stats, Money money) { - System.out.println(); - System.out.println("당첨 통계"); - System.out.println("---------"); - - Arrays.stream(Rank.winningRanks()) - .sorted(Comparator.comparingInt(Rank::displayOrder)) - .forEach(rank -> printRank(stats, rank)); - - System.out.println("총 수익률은 " + stats.profitRate(money) + "입니다."); - } - - private void printRank(WinningStatistics stats, Rank rank) { - System.out.println(rank.description() + " (" + rank.prize() + "원)- " + stats.countOf(rank) + "개"); - } -} diff --git a/src/main/java/lotto/ShuffleNumberGenerator.java b/src/main/java/lotto/ShuffleNumberGenerator.java deleted file mode 100644 index 1ad699bcd2..0000000000 --- a/src/main/java/lotto/ShuffleNumberGenerator.java +++ /dev/null @@ -1,16 +0,0 @@ -package lotto; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.IntStream; - -public class ShuffleNumberGenerator implements LottoNumberGenerator{ - @Override - public List generate() { - List numbers = IntStream.rangeClosed(1, 45) - .collect(ArrayList::new, List::add, List::addAll); - Collections.shuffle(numbers); - return numbers.subList(0, 6); - } -} diff --git a/src/main/java/lotto/application/LottoMachine.java b/src/main/java/lotto/application/LottoMachine.java new file mode 100644 index 0000000000..2fc94550fa --- /dev/null +++ b/src/main/java/lotto/application/LottoMachine.java @@ -0,0 +1,41 @@ +package lotto.application; + +import java.util.List; +import lotto.domain.generator.AutoLottosGenerator; +import lotto.domain.Lotto; +import lotto.domain.generator.CompositeLottosGenerator; +import lotto.domain.generator.LottoNumberGenerator; +import lotto.domain.Lottos; +import lotto.domain.generator.LottosGenerator; +import lotto.domain.generator.ManualLottosGenerator; +import lotto.domain.generator.MixedLottosGenerator; +import lotto.domain.Money; + +public class LottoMachine { + private final LottoNumberGenerator numberGenerator; + + public LottoMachine(LottoNumberGenerator numberGenerator) { + this.numberGenerator = numberGenerator; + } + + public Lottos issue(Money money) { + LottosGenerator auto = new AutoLottosGenerator(money.ticketCount(), numberGenerator); + return auto.generate(); } + + + public Lottos issue(Money money, List manualLottos) { + int totalCount = money.ticketCount(); + int manualCount = manualLottos.size(); + + if (manualCount > totalCount) { + throw new IllegalArgumentException("수동 로또 개수는 전체 구매 개수보다 클 수 없습니다."); + } + + int autoCount = totalCount - manualCount; + + LottosGenerator manual = new ManualLottosGenerator(manualLottos); + LottosGenerator auto = new AutoLottosGenerator(autoCount, numberGenerator); + + return CompositeLottosGenerator.of(manual, auto).generate(); + } +} diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/domain/Lotto.java similarity index 97% rename from src/main/java/lotto/Lotto.java rename to src/main/java/lotto/domain/Lotto.java index 5816f18847..4546188cf6 100644 --- a/src/main/java/lotto/Lotto.java +++ b/src/main/java/lotto/domain/Lotto.java @@ -1,6 +1,5 @@ -package lotto; +package lotto.domain; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/src/main/java/lotto/domain/LottoFactory.java b/src/main/java/lotto/domain/LottoFactory.java new file mode 100644 index 0000000000..b344fcd7c1 --- /dev/null +++ b/src/main/java/lotto/domain/LottoFactory.java @@ -0,0 +1,14 @@ +package lotto.domain; + +import java.util.List; +import java.util.stream.Collectors; + +public class LottoFactory { + public Lottos create(List> numbers) { + return new Lottos( + numbers.stream() + .map(Lotto::new) + .collect(Collectors.toList()) + ); + } +} diff --git a/src/main/java/lotto/LottoNumber.java b/src/main/java/lotto/domain/LottoNumber.java similarity index 96% rename from src/main/java/lotto/LottoNumber.java rename to src/main/java/lotto/domain/LottoNumber.java index d5063c33b8..e93c610a75 100644 --- a/src/main/java/lotto/LottoNumber.java +++ b/src/main/java/lotto/domain/LottoNumber.java @@ -1,7 +1,6 @@ -package lotto; +package lotto.domain; import java.util.Map; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; public class LottoNumber { diff --git a/src/main/java/lotto/Lottos.java b/src/main/java/lotto/domain/Lottos.java similarity index 56% rename from src/main/java/lotto/Lottos.java rename to src/main/java/lotto/domain/Lottos.java index bf7d7aec26..fb185878e3 100644 --- a/src/main/java/lotto/Lottos.java +++ b/src/main/java/lotto/domain/Lottos.java @@ -1,8 +1,7 @@ -package lotto; +package lotto.domain; -import java.util.HashSet; +import java.util.ArrayList; import java.util.List; -import java.util.Set; public class Lottos { private final List values; @@ -18,4 +17,10 @@ public List values() { public int size() { return values.size(); } + + public Lottos merge(Lottos other) { + List merged = new ArrayList<>(this.values); + merged.addAll(other.values); + return new Lottos(merged); + } } diff --git a/src/main/java/lotto/MatchResult.java b/src/main/java/lotto/domain/MatchResult.java similarity index 94% rename from src/main/java/lotto/MatchResult.java rename to src/main/java/lotto/domain/MatchResult.java index e42b00e6a1..a58ccdedb6 100644 --- a/src/main/java/lotto/MatchResult.java +++ b/src/main/java/lotto/domain/MatchResult.java @@ -1,4 +1,4 @@ -package lotto; +package lotto.domain; public class MatchResult { private final int matchCount; diff --git a/src/main/java/lotto/Money.java b/src/main/java/lotto/domain/Money.java similarity index 97% rename from src/main/java/lotto/Money.java rename to src/main/java/lotto/domain/Money.java index 2677a0b0b2..6b1115ac10 100644 --- a/src/main/java/lotto/Money.java +++ b/src/main/java/lotto/domain/Money.java @@ -1,4 +1,4 @@ -package lotto; +package lotto.domain; public class Money { private static final int UNIT_PRICE = 1_000; @@ -14,14 +14,14 @@ public static Money of(int amount) { return new Money(amount); } - public int ticketCount() { - return amount / UNIT_PRICE; - } - public int amount() { return amount; } + public int ticketCount() { + return amount / UNIT_PRICE; + } + private void validateAmount(int amount) { if (amount <= 0) { throw new IllegalArgumentException("구입 금액은 0보다 커야 한다."); diff --git a/src/main/java/lotto/Rank.java b/src/main/java/lotto/domain/Rank.java similarity index 96% rename from src/main/java/lotto/Rank.java rename to src/main/java/lotto/domain/Rank.java index 712d9aea54..f71ba05ff6 100644 --- a/src/main/java/lotto/Rank.java +++ b/src/main/java/lotto/domain/Rank.java @@ -1,8 +1,6 @@ -package lotto; +package lotto.domain; -import lotto.MatchResult; - public enum Rank { FIRST(6, false, 2_000_000_000, "6개 일치", 5), SECOND(5, true, 30_000_000, "5개 일치, 보너스 볼 일치", 4), @@ -50,5 +48,4 @@ public int displayOrder() { public static Rank[] winningRanks() { return new Rank[]{FIFTH, FOURTH, THIRD, SECOND, FIRST}; - } -} + }} diff --git a/src/main/java/lotto/WinningNumbers.java b/src/main/java/lotto/domain/WinningNumbers.java similarity index 96% rename from src/main/java/lotto/WinningNumbers.java rename to src/main/java/lotto/domain/WinningNumbers.java index 51d630a1bc..39a5b60969 100644 --- a/src/main/java/lotto/WinningNumbers.java +++ b/src/main/java/lotto/domain/WinningNumbers.java @@ -1,8 +1,6 @@ -package lotto; +package lotto.domain; -import lotto.MatchResult; - public class WinningNumbers { private final Lotto winning; private final LottoNumber bonus; diff --git a/src/main/java/lotto/WinningStatistics.java b/src/main/java/lotto/domain/WinningStatistics.java similarity index 74% rename from src/main/java/lotto/WinningStatistics.java rename to src/main/java/lotto/domain/WinningStatistics.java index 2af77bc588..0d3f59ae53 100644 --- a/src/main/java/lotto/WinningStatistics.java +++ b/src/main/java/lotto/domain/WinningStatistics.java @@ -1,4 +1,4 @@ -package lotto; +package lotto.domain; import java.util.EnumMap; import java.util.Map; @@ -22,7 +22,10 @@ public long totalPrize() { return sum; } - public double profitRate(Money purchase) { - return (double) totalPrize() / purchase.amount(); + public double profitRate(Money money) { + if (money.amount() == 0) { + return 0.0; + } + return (totalPrize() * 100.0) / money.amount(); } } diff --git a/src/main/java/lotto/domain/generator/AutoLottosGenerator.java b/src/main/java/lotto/domain/generator/AutoLottosGenerator.java new file mode 100644 index 0000000000..1786825c34 --- /dev/null +++ b/src/main/java/lotto/domain/generator/AutoLottosGenerator.java @@ -0,0 +1,28 @@ +package lotto.domain.generator; + +import java.util.ArrayList; +import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; + +public class AutoLottosGenerator implements LottosGenerator { + private final int count; + private final LottoNumberGenerator numberGenerator; + + public AutoLottosGenerator(int count, LottoNumberGenerator numberGenerator) { + if (count < 0) { + throw new IllegalArgumentException("자동 로또 개수는 0 이상이어야 합니다."); + } + this.count = count; + this.numberGenerator = numberGenerator; + } + + @Override + public Lottos generate() { + List lottos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + lottos.add(new Lotto(numberGenerator.generate())); + } + return new Lottos(lottos); + } +} diff --git a/src/main/java/lotto/domain/generator/CompositeLottosGenerator.java b/src/main/java/lotto/domain/generator/CompositeLottosGenerator.java new file mode 100644 index 0000000000..5a21de8170 --- /dev/null +++ b/src/main/java/lotto/domain/generator/CompositeLottosGenerator.java @@ -0,0 +1,31 @@ +package lotto.domain.generator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; + +public class CompositeLottosGenerator implements LottosGenerator { + private final List children; + + public CompositeLottosGenerator(List children) { + if (children == null || children.isEmpty()) { + throw new IllegalArgumentException("children must not be empty"); + } + this.children = List.copyOf(children); + } + + public static CompositeLottosGenerator of(LottosGenerator... children) { + return new CompositeLottosGenerator(Arrays.asList(children)); + } + + @Override + public Lottos generate() { + List merged = new ArrayList<>(); + for (LottosGenerator child : children) { + merged.addAll(child.generate().values()); // Lottos.values() 필요 + } + return new Lottos(merged); + } +} diff --git a/src/main/java/lotto/LottoNumberGenerator.java b/src/main/java/lotto/domain/generator/LottoNumberGenerator.java similarity index 75% rename from src/main/java/lotto/LottoNumberGenerator.java rename to src/main/java/lotto/domain/generator/LottoNumberGenerator.java index df7e19d516..00a085f376 100644 --- a/src/main/java/lotto/LottoNumberGenerator.java +++ b/src/main/java/lotto/domain/generator/LottoNumberGenerator.java @@ -1,4 +1,4 @@ -package lotto; +package lotto.domain.generator; import java.util.List; diff --git a/src/main/java/lotto/domain/generator/LottosGenerator.java b/src/main/java/lotto/domain/generator/LottosGenerator.java new file mode 100644 index 0000000000..2eeeb385f4 --- /dev/null +++ b/src/main/java/lotto/domain/generator/LottosGenerator.java @@ -0,0 +1,7 @@ +package lotto.domain.generator; + +import lotto.domain.Lottos; + +public interface LottosGenerator { + Lottos generate(); +} diff --git a/src/main/java/lotto/domain/generator/ManualLottosGenerator.java b/src/main/java/lotto/domain/generator/ManualLottosGenerator.java new file mode 100644 index 0000000000..27c8ba65ca --- /dev/null +++ b/src/main/java/lotto/domain/generator/ManualLottosGenerator.java @@ -0,0 +1,18 @@ +package lotto.domain.generator; + +import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; + +public class ManualLottosGenerator implements LottosGenerator { + private final List manualLottos; + + public ManualLottosGenerator(List manualLottos) { + this.manualLottos = List.copyOf(manualLottos); + } + + @Override + public Lottos generate() { + return new Lottos(manualLottos); + } +} diff --git a/src/main/java/lotto/domain/generator/MixedLottosGenerator.java b/src/main/java/lotto/domain/generator/MixedLottosGenerator.java new file mode 100644 index 0000000000..05408610ab --- /dev/null +++ b/src/main/java/lotto/domain/generator/MixedLottosGenerator.java @@ -0,0 +1,27 @@ +package lotto.domain.generator; + +import java.util.ArrayList; +import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; + +public class MixedLottosGenerator implements LottosGenerator { + private final LottosGenerator manual; + private final LottosGenerator auto; + + public MixedLottosGenerator(LottosGenerator manual, LottosGenerator auto) { + this.manual = manual; + this.auto = auto; + } + + @Override + public Lottos generate() { + Lottos manualIssued = manual.generate(); + Lottos autoIssued = auto.generate(); + + List merged = new ArrayList<>(manualIssued.values()); + merged.addAll(autoIssued.values()); + + return new Lottos(merged); + } +} diff --git a/src/main/java/lotto/domain/generator/ShuffleNumberGenerator.java b/src/main/java/lotto/domain/generator/ShuffleNumberGenerator.java new file mode 100644 index 0000000000..a03e43f18c --- /dev/null +++ b/src/main/java/lotto/domain/generator/ShuffleNumberGenerator.java @@ -0,0 +1,19 @@ +package lotto.domain.generator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ShuffleNumberGenerator implements LottoNumberGenerator { + private static final int LOTTO_SIZE = 6; + + @Override + public List generate() { + List numbers = new ArrayList<>(); + for (int i = 1; i <= 45; i++) { + numbers.add(i); + } + Collections.shuffle(numbers); + return numbers.subList(0, LOTTO_SIZE); + } +} diff --git a/src/main/java/lotto/ui/InputView.java b/src/main/java/lotto/ui/InputView.java new file mode 100644 index 0000000000..f6cdeb15b4 --- /dev/null +++ b/src/main/java/lotto/ui/InputView.java @@ -0,0 +1,46 @@ +package lotto.ui; + +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.stream.Collectors; +import lotto.domain.Lotto; + +public class InputView { + private final Scanner scanner = new Scanner(System.in); + + public int readPurchaseAmount() { + System.out.println("구입금액을 입력해 주세요."); + return Integer.parseInt(scanner.nextLine().trim()); + } + + public int readManualCount() { + System.out.println("수동으로 구매할 로또 수를 입력해 주세요."); + return Integer.parseInt(scanner.nextLine().trim()); + } + + public List readManualLottos(int manualCount) { + System.out.println("수동으로 구매할 번호를 입력해 주세요."); + // 예: 1,2,3,4,5,6 (manualCount줄) + return java.util.stream.IntStream.range(0, manualCount) + .mapToObj(i -> new Lotto(parseNumbers(scanner.nextLine().trim()))) + .collect(Collectors.toList()); + } + + public List readWinningNumbers() { + System.out.println("당첨 번호를 입력해 주세요."); + return parseNumbers(scanner.nextLine().trim()); + } + + public int readBonusNumber() { + System.out.println("보너스 볼을 입력해 주세요."); + return Integer.parseInt(scanner.nextLine().trim()); + } + + private List parseNumbers(String line) { + return Arrays.stream(line.split(",")) + .map(String::trim) + .map(Integer::parseInt) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/lotto/ui/LottoController.java b/src/main/java/lotto/ui/LottoController.java new file mode 100644 index 0000000000..805fadea3f --- /dev/null +++ b/src/main/java/lotto/ui/LottoController.java @@ -0,0 +1,86 @@ +package lotto.ui; + + +import java.util.List; +import lotto.domain.Lotto; +import lotto.application.LottoMachine; +import lotto.domain.Lottos; +import lotto.domain.Money; +import lotto.domain.WinningNumbers; +import lotto.domain.WinningStatistics; + +public class LottoController { + private final InputView inputView; + private final ResultView resultView; + private final LottoMachine machine; + + public LottoController(InputView inputView, ResultView resultView, LottoMachine machine) { + this.inputView = inputView; + this.resultView = resultView; + this.machine = machine; + } + + public void run() { + Money money = readMoney(); + + int manualCount = readManualCount(money); + List manualLottos = readManualLottos(manualCount); + + Lottos issued = machine.issue(money, manualLottos); + + resultView.printPurchaseSummary(manualCount, money.ticketCount() - manualCount); + resultView.printLottos(issued); + + WinningNumbers winningNumbers = readWinningNumbers(); + WinningStatistics stats = winningNumbers.match(issued); + + resultView.printStatistics(stats); + resultView.printProfitRate(stats.profitRate(money)); + } + + private Money readMoney() { + while (true) { + try { + return Money.of(inputView.readPurchaseAmount()); + } catch (IllegalArgumentException e) { + resultView.printError(e.getMessage()); + } + } + } + + private int readManualCount(Money money) { + while (true) { + try { + int manualCount = inputView.readManualCount(); + if (manualCount < 0 || manualCount > money.ticketCount()) { + throw new IllegalArgumentException("수동 로또 개수는 0 이상이며 전체 구매 개수 이하여야 합니다."); + } + return manualCount; + } catch (IllegalArgumentException e) { + resultView.printError(e.getMessage()); + } + } + } + + private List readManualLottos(int manualCount) { + while (true) { + try { + return inputView.readManualLottos(manualCount); + } catch (IllegalArgumentException e) { + resultView.printError(e.getMessage()); + } + } + } + + private WinningNumbers readWinningNumbers() { + while (true) { + try { + Lotto winning = new Lotto(inputView.readWinningNumbers()); + int bonus = inputView.readBonusNumber(); + return new WinningNumbers(winning, bonus); + } catch (IllegalArgumentException e) { + resultView.printError(e.getMessage()); + } + } + } +} diff --git a/src/main/java/lotto/ui/ResultView.java b/src/main/java/lotto/ui/ResultView.java new file mode 100644 index 0000000000..00a6019860 --- /dev/null +++ b/src/main/java/lotto/ui/ResultView.java @@ -0,0 +1,39 @@ +package lotto.ui; + +import java.util.Arrays; +import java.util.Comparator; +import lotto.domain.Lottos; +import lotto.domain.Rank; +import lotto.domain.WinningStatistics; + +public class ResultView { + public void printPurchaseSummary(int manualCount, int autoCount) { + System.out.println("수동으로 " + manualCount + "장, 자동으로 " + autoCount + "장을 구매했습니다."); + } + + public void printLottos(Lottos lottos) { + // 기존 출력 규칙이 따로 있으면 여기만 맞추면 됨 + lottos.values().forEach(lotto -> System.out.println(lotto.numbers())); + } + + public void printStatistics(WinningStatistics stats) { + System.out.println("당첨 통계"); + System.out.println("---------"); + + Arrays.stream(Rank.winningRanks()) + .sorted(Comparator.comparingInt(Rank::displayOrder)) + .forEach(rank -> printRank(stats, rank)); + } + + public void printProfitRate(double profitRate) { + System.out.printf("총 수익률은 %.1f%%입니다.%n", profitRate); + } + + public void printError(String message) { + System.out.println("[ERROR] " + message); + } + + private void printRank(WinningStatistics stats, Rank rank) { + System.out.println(rank.description() + " (" + rank.prize() + "원)- " + stats.countOf(rank) + "개"); + } +} diff --git a/src/test/java/lotto/CompositeLottosGeneratorTest.java b/src/test/java/lotto/CompositeLottosGeneratorTest.java new file mode 100644 index 0000000000..4ca9ac7b1e --- /dev/null +++ b/src/test/java/lotto/CompositeLottosGeneratorTest.java @@ -0,0 +1,24 @@ +package lotto; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; +import lotto.domain.generator.CompositeLottosGenerator; +import lotto.domain.generator.LottosGenerator; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class CompositeLottosGeneratorTest { + @Test + @DisplayName("Composite는 여러 Generator의 결과를 합쳐 하나의 Lottos로 만든다") + void compositeMergesChildren() { + LottosGenerator a = () -> new Lottos(List.of(new Lotto(List.of(1,2,3,4,5,6)))); + LottosGenerator b = () -> new Lottos(List.of(new Lotto(List.of(7,8,9,10,11,12)))); + + Lottos merged = CompositeLottosGenerator.of(a, b).generate(); + + assertThat(merged.size()).isEqualTo(2); + } +} diff --git a/src/test/java/lotto/FakeNumberGenerator.java b/src/test/java/lotto/FakeNumberGenerator.java index 7f1b97c2a2..5ffaef74ae 100644 --- a/src/test/java/lotto/FakeNumberGenerator.java +++ b/src/test/java/lotto/FakeNumberGenerator.java @@ -1,17 +1,24 @@ package lotto; import java.util.List; +import lotto.domain.generator.LottoNumberGenerator; -public class FakeNumberGenerator implements LottoNumberGenerator{ +public class FakeNumberGenerator implements LottoNumberGenerator { private final List> fixed; private int index = 0; public FakeNumberGenerator(List> fixed) { + if (fixed == null || fixed.isEmpty()) { + throw new IllegalArgumentException("fixed numbers must not be empty"); + } this.fixed = fixed; } @Override public List generate() { + if (index >= fixed.size()) { + throw new IllegalStateException("No more fixed numbers. index=" + index); + } return fixed.get(index++); } } diff --git a/src/test/java/lotto/LottoMachineTest.java b/src/test/java/lotto/LottoMachineTest.java index c2a4a50347..3bbd4f7d69 100644 --- a/src/test/java/lotto/LottoMachineTest.java +++ b/src/test/java/lotto/LottoMachineTest.java @@ -1,8 +1,14 @@ package lotto; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import java.util.List; +import lotto.application.LottoMachine; +import lotto.domain.Lotto; +import lotto.domain.generator.LottoNumberGenerator; +import lotto.domain.Lottos; +import lotto.domain.Money; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,8 +17,8 @@ public class LottoMachineTest { @DisplayName("구입 금액에 맞는 개수의 로또를 발급한다") void issueLottosByMoney() { LottoNumberGenerator generator = new FakeNumberGenerator(List.of( - List.of(1,2,3,4,5,6), - List.of(7,8,9,10,11,12) + List.of(1, 2, 3, 4, 5, 6), + List.of(7, 8, 9, 10, 11, 12) )); LottoMachine machine = new LottoMachine(generator); @@ -20,4 +26,44 @@ void issueLottosByMoney() { assertThat(lottos.size()).isEqualTo(2); } + + @Test + @DisplayName("수동 로또가 있으면 수동 + 자동을 합쳐 전체 구매 개수를 맞춘다") + void issueMixedLottos() { + LottoNumberGenerator generator = new FakeNumberGenerator(List.of( + List.of(1, 2, 3, 4, 5, 6), + List.of(7, 8, 9, 10, 11, 12), + List.of(13, 14, 15, 16, 17, 18) + )); + + LottoMachine machine = new LottoMachine(generator); + + Money money = Money.of(5000); + List manual = List.of( + new Lotto(List.of(21, 22, 23, 24, 25, 26)), + new Lotto(List.of(31, 32, 33, 34, 35, 36)) + ); + Lottos issued = machine.issue(money, manual); + + assertThat(issued.size()).isEqualTo(5); + assertThat(issued.values()) + .anySatisfy(l -> assertThat(l.numbers()).containsExactly(21, 22, 23, 24, 25, 26)) + .anySatisfy(l -> assertThat(l.numbers()).containsExactly(31, 32, 33, 34, 35, 36)); + } + + @Test + @DisplayName("수동 로또 개수가 전체 구매 개수보다 많으면 예외") + void manualCountCannotExceedTotal() { + LottoNumberGenerator generator = () -> List.of(1, 2, 3, 4, 5, 6); + LottoMachine machine = new LottoMachine(generator); + + Money money = Money.of(1000); + List manual = List.of( + new Lotto(List.of(1, 2, 3, 4, 5, 6)), + new Lotto(List.of(7, 8, 9, 10, 11, 12)) + ); + + assertThatThrownBy(() -> machine.issue(money, manual)) + .isInstanceOf(IllegalArgumentException.class); + } } diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java index 894bd2aeb5..a355a686e5 100644 --- a/src/test/java/lotto/LottoTest.java +++ b/src/test/java/lotto/LottoTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import java.util.List; +import lotto.domain.Lotto; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/lotto/LottosTest.java b/src/test/java/lotto/LottosTest.java index cfda6385a2..8027afbf92 100644 --- a/src/test/java/lotto/LottosTest.java +++ b/src/test/java/lotto/LottosTest.java @@ -5,6 +5,8 @@ import java.util.ArrayList; import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/lotto/MoneyTest.java b/src/test/java/lotto/MoneyTest.java index 9e7b0c4d70..59060fb95f 100644 --- a/src/test/java/lotto/MoneyTest.java +++ b/src/test/java/lotto/MoneyTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import lotto.domain.Money; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/lotto/RankTest.java b/src/test/java/lotto/RankTest.java index 756244c790..f521ed6339 100644 --- a/src/test/java/lotto/RankTest.java +++ b/src/test/java/lotto/RankTest.java @@ -2,6 +2,8 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import lotto.domain.MatchResult; +import lotto.domain.Rank; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/lotto/WinningNumbersTest.java b/src/test/java/lotto/WinningNumbersTest.java index 70ec2a800a..d853580e6a 100644 --- a/src/test/java/lotto/WinningNumbersTest.java +++ b/src/test/java/lotto/WinningNumbersTest.java @@ -4,6 +4,11 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; +import lotto.domain.Rank; +import lotto.domain.WinningNumbers; +import lotto.domain.WinningStatistics; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -27,20 +32,4 @@ void matchReturnsRank() { assertThat(rank).isEqualTo(Rank.SECOND); } - - @Test - @DisplayName("WinningNumbers.match(lottos)은 통계를 만들어 반환한다") - void matchLottosReturnsStatistics() { - WinningNumbers winningNumbers = new WinningNumbers(new Lotto(List.of(1,2,3,4,5,6)), 7); - - Lottos tickets = new Lottos(List.of( - new Lotto(List.of(1,2,3,4,5,7)), // SECOND - new Lotto(List.of(1,2,3,4,5,10)) // THIRD - )); - - WinningStatistics stats = winningNumbers.match(tickets); - - assertThat(stats.countOf(Rank.SECOND)).isEqualTo(1); - assertThat(stats.countOf(Rank.THIRD)).isEqualTo(1); - } } diff --git a/src/test/java/lotto/WinningStatisticsTest.java b/src/test/java/lotto/WinningStatisticsTest.java index fc43e69a30..0c983af05e 100644 --- a/src/test/java/lotto/WinningStatisticsTest.java +++ b/src/test/java/lotto/WinningStatisticsTest.java @@ -3,6 +3,12 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import java.util.List; +import lotto.domain.Lotto; +import lotto.domain.Lottos; +import lotto.domain.Money; +import lotto.domain.Rank; +import lotto.domain.WinningNumbers; +import lotto.domain.WinningStatistics; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,7 +31,7 @@ void totalPrize() { } @Test - @DisplayName("수익률은 총 당첨금을 구입 금액으로 나눈 값이다") + @DisplayName("수익률(%)은 (총 당첨금 / 구입 금액) * 100 이다") void profitRate() { WinningNumbers winningNumbers = new WinningNumbers(new Lotto(List.of(1,2,3,4,5,6)), 7); @@ -36,7 +42,7 @@ void profitRate() { WinningStatistics stats = winningNumbers.match(tickets); Money purchase = Money.of(10_000); - double expected = (double) Rank.SECOND.prize() / purchase.amount(); + double expected = (double) Rank.SECOND.prize() * 100 / purchase.amount(); assertThat(stats.profitRate(purchase)).isEqualTo(expected); }