๐Ÿ•น Level 1. ๋กœ๋˜ ๋ฏธ์…˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์ •๋ฆฌ


1๋‹จ๊ณ„ ํ”ผ๋“œ๋ฐฑ

๊ฐ์ฒด์ธ Controller

LottoFactory๋ฅผ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ ๋‘์ง€ ์•Š์•˜๋˜ ์ด์œ ๋Š” Controller๋Š” ๊ฐ์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๋ฉด ์•ˆ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.
๋‹น์—ฐํžˆ Domain๋งŒ ๊ฐ์ฒด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์—ˆ๋‹ค. (๋ชจ๋ฅด๋Š”๊ฑด ๋ถ€๋„๋Ÿฌ์šด๊ฒŒ ์•„๋‹ˆ๋‹ค ๐Ÿฅฒ)

ํ•˜์ง€๋งŒ ์ƒ๊ฐํ•ด๋ณด๋‹ˆ Controller๋„ ๋„๋ฉ”์ธ๊ณผ ๋ทฐ๋ฅผ ์ด์–ด์ฃผ๋Š” ๋™์‹œ์—, ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฑ…์ž„์ด ์žˆ๋‹ค.
์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๊ฒŒ ๋œ๋‹ค๋ฉด getLottoTickets ๋ฉ”์†Œ๋“œ์—์„œ ๋งค๋ฒˆ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

์ผ๋‹จ ์ด ์ฝ”๋“œ์—์„œ ์ค‘์š”ํ•œ ์ ์€ ์–ด์ฐจํ”ผ ์ง€๊ธˆ ๋กœ๋˜๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฑ…์ž„์„ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ•˜๊ณ  ์žˆ์—ˆ์œผ๋‹ˆ
โ€œ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ๋ฐฉ์ง€ํ•˜๋„๋ก ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ๋งŒ๋“ค์–ด์„œ ์žฌํ™œ์šฉํ•˜์žโ€๊ฐ€ ๋ชฉ์ ์ด๋‹ค.

๋ฏธ์…˜ ๋ฐ๋“œ๋ผ์ธ์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด ์ข€ ๋” ๊ผผ๊ผผํžˆ ํ™•์ธ์„ ๋ชปํ–ˆ๋˜ ๋ถ€๋ถ„์ธ๋ฐ,
๋กœ๋˜ ํ‹ฐ์ผ“์„ ๋งŒ๋“œ๋Š” LottoTicketFactory์— ์žˆ๋Š” ์ƒ์ˆ˜ ๊ฐ’๋“ค์ด LottoNumber๋‚˜ LottoTicket๋“ฑ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์ค‘๋ณต๋˜์–ด ์“ฐ์ด๊ณ  ์žˆ์—ˆ๋‹ค.
๋˜ ์‹ฌ์ง€์–ด ๋กœ๋˜์˜ MAX ๋ฒˆํ˜ธ๋Š” 45์ธ๋ฐ, 49๊นŒ์ง€๋กœ ์ž…๋ ฅํ•ด ๋†“์•˜๋‹ค ๐Ÿ™ƒ
๋•๋ถ„์— ์ƒ์ˆ˜๊ฐ’ ํ•˜๋‚˜๋ผ๋„ ๊ผผ๊ผผํžˆ ํ™•์ธํ•ด์•ผ๊ฒ ๋‹ค๋Š” ๊นจ๋‹ฌ์Œ์„ ์–ป์—ˆ๋‹ค.

์ค‘๋ณต๋˜๋Š” ์ƒ์ˆ˜๋ฅผ ๋ง‰์œผ๋ฉฐ, ๊ฐ ๊ฐ์ฒด๊ฐ€ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•˜๋Š” ๊ฐ’๋Œ€๋กœ ์ƒ์ˆ˜๋ฅผ ๋‘๊ณ 
์ด๋ฅผ import ํ•ด์„œ ์‚ฌ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ๋‹ค.

LottoNumber

public class LottoNumber implements Comparable<LottoNumber> {
    public static final int MIN_LOTTO_NUMBER = 1;
    public static final int MAX_LOTTO_NUMBER = 45;
    private static final Pattern NUMBER_PATTERN = Pattern.compile("^[0-9]*$");

LottoTicket

public class LottoTicket {
    public static final int LOTTO_TICKET_SIZE = 6;

LottoFactory

import static lotto.domain.LottoNumber.MAX_LOTTO_NUMBER;
import static lotto.domain.LottoNumber.MIN_LOTTO_NUMBER;
import static lotto.domain.LottoTicket.LOTTO_TICKET_SIZE;
import static lotto.domain.Money.LOTTO_PRICE;

public class LottoTicketFactory {
    private static final int START_INDEX = 0;

๋ฆฌํŒฉํ† ๋ง ์ „ ๋กœ์ง์œผ๋กœ๋Š” ๊ตฌ๋งค ๊ธˆ์•ก์„ ์ž…๋ ฅ ๋ฐ›์„ ๋•Œ ๋งŒ์•ฝ 14500์›์ด ๋“ค์–ด์˜ค๋ฉด ์‚ฌ์šฉํ•œ ๊ธˆ์•ก์€
14์žฅ์„ ์‚ฌ๋Š”๋ฐ 14000์›๋งŒ ๋“ค๊ฒŒ ํ•˜๊ณ  500์›์ด ๋‚จ๋Š” ๊ฒƒ์€ ์˜ˆ์™ธ๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ์ง„ํ–‰ํ–ˆ๋‹ค.
๊ทธ๋Ÿฐ๋ฐ ์ด ๋กœ์ง์€ ๊ตฌ๋งคํ•œ ๊ธˆ์•ก์ด ์•„๋‹Œ ์ง€๋ถˆํ•œ 14500์›์œผ๋กœ ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•˜๊ณ  ์žˆ๋‹ค.
๋•Œ๋ฌธ์— ์ž…๋ ฅ ๊ธˆ์•ก๊ณผ ๊ตฌ๋งค ๊ธˆ์•ก์˜ ๋ฏธ์ผ์น˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

ํ”ผ๋“œ๋ฐฑ์„ ํ†ตํ•ด lottoResult์—์„œ ๊ตฌํ•  ์ˆ˜ ์žˆ๋Š” ํ‹ฐ์ผ“ ๊ฐœ์ˆ˜์™€ ์ƒ์ˆ˜ ๋ณ€์ˆ˜์ธ ๋กœ๋˜์˜ ๊ตฌ๋งค์žฅ์ˆ˜๋กœ
์ง€๋ถˆ ๊ธˆ์•ก์„ ๊ตฌํ•  ์ˆ˜ ์žˆ์Œ์„ ๊นจ๋‹ซ๊ณ  showResult() ์—์„œ money๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์„ ํ•„์š”๊ฐ€ ์—†์Œ์„ ๊นจ๋‹ฌ์•˜๋‹ค.

๋•๋ถ„์— money์™€์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ํ•˜๋‚˜ ๋Š์„ ์ˆ˜ ์žˆ๊ณ ,
์ˆ˜์ต๊ธˆ ๊ณ„์‚ฐ ์‹œ ์ž…๋ ฅ ๊ธˆ์•ก๊ณผ ๊ตฌ๋งค ๊ธˆ์•ก ๋ฏธ์ผ์น˜๋กœ ์ธํ•œ ์ˆ˜์ต๋ฅ  ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

Prize์—์„œ์˜ static ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ

๋ฆฌํŒฉํ† ๋ง ์ „ Prize

    public static Prize getPrizeType(int matchCount, boolean isBonusBall) {
        if (isMatchCountEqualsPivot(matchCount) && isBonusBall) {
            return SECOND_PRIZE;
        }
        return Arrays.stream(values())
                .filter(s -> s.matchCount == matchCount)
                .findFirst()
                .orElse(NO_PRIZE);
    }

    private static boolean isMatchCountEqualsPivot(int matchCount) {
        return matchCount == BONUS_CHECK_PIVOT;
    }

    public static double calculatePrizeMoneySum(List<Prize> lottoResults, Money money) {
        Money moneySum = Money.ZERO;
        for (Prize prize : Prize.values()) {
            Money perPrizeMoneySum = prize.prizeMoney
                    .multiple(getCountByPrizeType(lottoResults, prize));
            moneySum = moneySum.plus(perPrizeMoneySum);
        }
        return moneySum.getRate(money);
    }

    public static int getCountByPrizeType(List<Prize> lottoResults, Prize prize) {
        return (int) lottoResults.stream()
                .filter(p -> p.equals(prize))
                .count();
    }

๋ฆฌํŒฉํ† ๋ง ์ „ LottoResult

public class LottoResult {
    private final List<Prize> lottoResults;

    public LottoResult(List<Prize> lottoResults) {
        this.lottoResults = new ArrayList<>(lottoResults);
    }

    public double calculateProfitRate(Money money) {
        return Prize.calculatePrizeMoneySum(lottoResults, money);
    }

    public int getCountPerPrizeType(Prize prize) {
        return Prize.getCountByPrizeType(lottoResults,prize);
    }
}

LottoResult์—์„œ calculateProfitRate()์„ ํ•˜๊ธฐ ์œ„ํ•ด Prize์˜ ๋„์›€์„ ๋ฐ›์•˜๋‹ค.
lottoResults๊ฐ€ Prize์˜ ๋„์›€์„ ๋ฐ›์ง€ ์•Š๊ณ  List<Prize> lottoResults ๊ฐ๊ฐ์˜ prize์—๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด ์Šค์Šค๋กœ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ–ˆ์—ˆ๋‹ค.

LottoResult

    public Money getTotalProfit() {
        Money totalProfit = Money.ZERO;
        for (Prize prize : lottoResults) {
            totalProfit = totalProfit.plus(prize.getPrizeMoney());
        }
        return totalProfit;
    }

ํ•˜์ง€๋งŒ ๋ฆฌํŒฉํ† ๋ง ํ›„์—๋„ ์•„์ง ๊ฐ์ฒด ์Šค์Šค๋กœ ํ•  ์ˆ˜ ์žˆ๋Š”์ผ๋“ค์ด ๋‚จ์•„์žˆ์—ˆ๋‹ค.

๋˜ ์ด๋•Œ๊นŒ์ง€ ๋กœ๋˜ ํ‹ฐ์ผ“๊ณผ ๋‹น์ฒจ ํ‹ฐ์ผ“์„ ๋น„๊ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฑ…์ž„์„
lott. Tickets๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ์—ˆ๋‹ค.
๊ณฐ๊ณฐํžˆ ์ƒ๊ฐํ•ด๋ณด๋‹ˆ ๋กœ๋˜ ํ‹ฐ์ผ“๊ณผ ๋‹น์ฒจ ํ‹ฐ์ผ“์„ ๋น„๊ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฑ…์ž„์€ winningLotto๊ฐ€ ๋งก๋Š” ๊ฒƒ์ด ๋” ์ ํ•ฉํ•˜๋‹ค๊ณ  ํŒ๋‹จ์ด ๋˜์—ˆ๊ณ ,
winningLotto์—์„œ prize๋ฅผ ์ฒดํฌํ•˜๋„๋ก ์ฑ…์ž„์„ ์ด๋™ํ•˜์˜€๋‹ค.

์ตœ์ข…์ ์œผ๋กœ LottoResult๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์€ winnigLotto์—์„œ ๋‹ด๋‹นํ•˜๋ฉฐ,
๋˜ Prize์˜ ๋„์›€์„ ๋ฐ›์ง€ ์•Š๊ณ  ์Šค์Šค๋กœ prize๋ฅผ ์ฒดํฌํ•˜๋„๋ก ํ•˜์˜€๋‹ค.

๋ฆฌํŒฉํ† ๋งํ•œ WinnigLotto์˜ ๋ฉ”์†Œ๋“œ ์ผ๋ถ€

public Prize matchPrize(LottoTicket lottoTicket) {
    int matchCount = getMatchingCount(lottoTicket);
    boolean isBonusNumber = isContainBonusNumber(lottoTicket);
    return Prize.findPrize(matchCount, isBonusNumber);
}

private int getMatchingCount(LottoTicket lottoTicket) {
    return (int) lottoTicket.lottoTicket().stream()
            .filter(winningTicket.lottoTicket()::contains)
            .count();
}

private boolean isContainBonusNumber(LottoTicket lottoTicket) {
    return lottoTicket.lottoTicket()
            .stream()
            .anyMatch(lottoNumber -> lottoNumber.equals(bonusNumber));
}

๋ฆฌํŒฉํ† ๋ง ํ›„ Prize์—์„œ ์ค„์–ด๋“  static ๋ฉ”์†Œ๋“œ๋“ค

public static Prize findPrize(int matchCount, boolean isBonusNumber) {
    if (isMatchCountEqualsPivot(matchCount) && isBonusNumber) {
        return SECOND_PRIZE;
    }

    return Arrays.stream(values())
            .filter(s -> s.matchCount == matchCount)
            .findFirst()
            .orElse(NO_PRIZE);
}

private static boolean isMatchCountEqualsPivot(int matchCount) {
    return matchCount == BONUS_CHECK_PIVOT;
}

public Money getPrizeMoney() {
    return prizeMoney;
}

public int getMatchCount() {
    return matchCount;
}

collectingAndThen

๋ฆฌ๋ทฐ์–ด๋‹˜๊ป˜์„œ ์ˆ˜์ •ํ•ด์ฃผ์‹  ์ฝ”๋“œ๋Š” collectingAndThen๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ,
์ฒ˜์Œ๋ณด๋Š” ๋ฉ”์†Œ๋“œ๋ผ ์ฐพ์•„๋ณด๋‹ˆ collecting์„ ์ง„ํ–‰ํ•œ ํ›„ ๊ทธ ๊ฒฐ๊ณผ๋กœ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ ์˜€๋‹ค.
stream map์„ ์จ์„œ ๊ฐ์ฒด ํ•˜๋‚˜ํ•˜๋‚˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฅผ ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ”๊ฟ”,
๋˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋‚˜์˜ ๋กœ์ง์—์„œ ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด
์ข€ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
์‚ฌ์šฉ๋ฒ•์„ ๋ฐฐ์› ์œผ๋‹ˆ, ์•ž์œผ๋กœ ์ž์ฃผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค ๐Ÿ‘€
๋˜ Collections์˜ ๋‹ค์–‘ํ•œ API๋ฅผ ์‚ดํŽด๋ณด์•„์•ผ๊ฒ ๋‹ค.

2๋‹จ๊ณ„ ์ดˆ๊ธฐ ๋ฆฌํŒฉํ† ๋ง

LottoCount ๊ฐ์ฒด ์ถ”๊ฐ€

์ด๋ฒˆ ๋ฆฌํŒฉํ† ๋ง์—์„œ ์ˆ˜๋™ ๋กœ๋˜ ๊ฐœ์ˆ˜๋ฅผ ์ž…๋ ฅ ๋ฐ›๊ณ , ์ด ๊ฐฏ์ˆ˜ ๋งŒํผ ๋กœ๋˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ถœ๋ ฅํ•ด์•ผํ–ˆ๋‹ค.
์›์‹œ๊ฐ’ ํฌ์žฅ์˜ ์˜๋ฏธ๋„ ๋‹ด์œผ๋ฉด์„œ ์ž…๋ ฅ ๋ฐ›๋Š” ์ˆ˜๋™ ๋กœ๋˜ ๊ฐฏ์ˆ˜์— ๋Œ€ํ•œ ๊ฒ€์ฆํ•˜๊ณ ,
์ด์— ๋Œ€ํ•œ ์ƒํƒœ์™€ ํ–‰์œ„๋ฅผ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌ ํ•˜๊ธฐ ์œ„ํ•ด LottoCount ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋˜์—ˆ๋‹ค.

public class LottoCount {
    private final int manualCount;
    private final int autoCount;

    public LottoCount(int manualCount, int totalCount) {
        validateLottoCount(manualCount, totalCount);
        this.manualCount = manualCount;
        this.autoCount = totalCount - this.manualCount;
    }

    private void validateLottoCount(int manualCount, int totalCount) {
        if (manualCount > totalCount || manualCount < 0) {
            throw new IllegalArgumentException("[ERROR] ๊ตฌ๋งค ๊ฐ€๋Šฅํ•œ ๋กœ๋˜ ๊ฐœ์ˆ˜ ๋ฒ”์œ„๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.");
        }
    }

    public int getManualCount() {
        return manualCount;
    }

    public int getAutoCount() {
        return autoCount;
    }
}

Exception

ํ™”์š”์ผ ๊ฐ•์˜์ธ Exception์ด ๋‚ด๊ฒŒ๋Š” ๋งŽ์€ ๋ฐฐ์›€์„ ์ฃผ์–ด์„œ,
์ด๋ฒˆ ์š”๊ตฌ์‚ฌํ•ญ์ธ ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’์— ๋Œ€ํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ์‹ ๊ฒฝ์ผ๋‹ค.
์ด์ „์—๋Š” Custom Exception์„ ๋งŒ๋“ค์–ด์„œ ๊ฐ ๊ฐ์ฒด๋งˆ๋‹ค ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์—ˆ์œผ๋‚˜,

  • ๋ฆฌํŒฉํ† ๋ง ์ „ Custom Class ```java package lotto.exception;

import lotto.view.ErrorView;

public class IllegalMoneyException extends IllegalArgumentException { public IllegalMoneyException() { ErrorView.printIllegalMoneyErrorMessage(); } }

๊ฐ Custom Exception ํด๋ž˜์Šค๊ฐ€ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ผ์ด ์—†๊ณ ,   
๋ฉ”์„ธ์ง€๋„ super์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ `ErrorView`๋ผ๋Š” ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ถœ๋ ฅํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—    
ํ•„์š”์„ฑ์„ ๋ชป๋Š๋ผ๊ฒŒ ๋˜์–ด ์ž๋ฐ”์˜ ๊ธฐ๋ณธ ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ–ˆ๋‹ค.  
๋˜ ๊ฐ์ฒด ๋‹จ์œ„๋กœ ๋ฌถ์–ด์„œ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด๋˜ ๋ถ€๋ถ„์€   
๊ฐ ์˜ˆ์™ธ ์‚ฌํ•ญ์— ๋งž๊ฒŒ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋„๋ก ์ˆ˜์ •ํ–ˆ๋‹ค.  

#### LottoTicketFactory -> AutoNumbersFactory
์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ `LottoTickets`๋ฅผ ์ƒ์„ฑํ•ด๋‚ด๋˜ LottoTicketfactory์˜ ์—ญํ• ์— ๋Œ€ํ•œ ์˜๋ฌธ์ด ๋“ค์—ˆ๋‹ค.   
์ฒ˜์Œ ๋ฆฌํŒฉํ† ๋ง์—์„œ๋Š” ๊ฐ์ฒด๋ฅผ ๋งŽ์ด ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด ์ˆ˜๋™ ๋กœ๋˜ ๋ฒˆํ˜ธ๋“ค์„ ๋ฐ›๊ณ  ์ž๋™ ๋กœ๋˜ ๊ฐฏ์ˆ˜๋ฅผ ๋ฐ›์•„์„œ  
`LottoTicketFactory`์—์„œ ์ž๋™ ๋กœ๋˜ ์ˆซ์ž๋“ค์„ ๋งŒ๋“ค๊ณ  `LottoTickets`๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ๋กœ ์ง„ํ–‰ํ–ˆ๋Š”๋ฐ,  
๋กœ๋˜ ํ‹ฐ์ผ“๋“ค ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋˜ ๋‹ค๋ฅธ `LottoTicketFactory`๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์ง€ ๋ชปํ•œ ๋ฐฉ์•ˆ์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.  

๋•Œ๋ฌธ์— LottoTickets ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋กœ๋˜ ๋ฒˆํ˜ธ๋“ค์˜ ๋ฆฌ์ŠคํŠธ๋“ค์„ ๋ฐ›์•„์„œ   
(`List<List<LottoNumber>> lottoNumbersGroup`) `LottoTickets` ์•ˆ์—์„œ ๋กœ๋˜ ํ‹ฐ์ผ“๋“ค์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด `LottoTicketFactory`๋Š” `AutoNumbersFactory`๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ  
์ž๋™ ๋กœ๋˜ ์ˆซ์ž ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ์—ญํ• ๋กœ ๋ณ€๊ฒฝํ•ด๋ณด์•˜๋‹ค !   

#### LottoNumber
0223 ์ˆ˜์—… ๋•Œ ๋กœ๋˜ ๋„˜๋ฒ„์˜ ๋ฒ”์œ„์— ์ œํ•œ์ด ์žˆ์œผ๋‹ˆ, ์ด๋ฅผ ๋ฏธ๋ฆฌ ์บ์‹ฑํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•ด๋ณด๋ผ๊ณ  ํ•˜์…จ๋‹ค.  
์ƒ๊ฐ์„ ํ•ด๋ณด๊ณ  ์บ์‹ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด๋‹ˆ `HashMap`์„ ์ด์šฉํ•ด์„œ ์บ์‹ฑ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ์„ ๊นจ๋‹ฌ์•˜๋‹ค.  
์ด๋กœ ์ƒ์„ฑ์ž๋ฅผ `private`๋กœ ๋ง‰์•„ ๋ถˆํ•„์š”ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ๋ง‰๊ณ ,   
`static block`์— ์˜ํ•ด ๋ฏธ๋ฆฌ ์บ์‹ฑ๋œ `LottoNumber`์—์„œ ํ‚ค๊ฐ’์œผ๋กœ ์ ‘๊ทผํ•ด ํ•ด๋‹น ๋กœ๋˜๋„˜๋ฒ„๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ˜•์‹์œผ๋กœ ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ–ˆ๋‹ค.  

* LottoNumber ์บ์‹œ ์ ์šฉ ๋ถ€๋ถ„
```java
static {
    IntStream.range(MIN_LOTTO_NUMBER, MAX_LOTTO_NUMBER + 1)
            .forEach(i -> CACHE_LOTTO_NUMBERS.put(i, new LottoNumber(i)));
}

public static LottoNumber valueOf(int index) {
    validateLottoNumber(index);
    return CACHE_LOTTO_NUMBERS.get(index);
}

2๋‹จ๊ณ„ ํ”ผ๋“œ๋ฐฑ

์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์†Œ๋“œ

์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ์ •๋ฆฌ๋Š” ํ–ˆ์—ˆ์œผ๋‚˜,
์‚ฌ์‹ค ์–ธ์ œ ์จ์•ผํ•˜๊ณ  ์–ธ์ œ ์“ฐ์ง€ ๋ง์•„์•ผํ• ์ง€ ์•„์ง ๊ฐ์ด ์•ˆ์™”๋‹ค ํ•ด์•ผํ•˜๋‚˜ ๐Ÿฅฒ

๊ทธ๋Ÿฐ๋ฐ ํฌ๋ฃจ๋“ค์ด๋ž‘ ์–˜๊ธฐ๋ฅผ ํ•ด๋ณด๋‹ˆ,
์ •๋‹ต์€ ์—†์œผ๋‚˜ ์ž์‹ ์ด ์“ฐ๋Š” ๋ช…๋ถ„์„ ๊ฐ€์ง€๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ๊ฒฐ๋ก ์„ ๋‚ด๋ ธ๋‹ค.
์‚ฌ์‹ค ์ด๋•Œ๊นŒ์ง€ ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ์‚ฌ์šฉ์„ฑ์„ ์ œ๋Œ€๋กœ ์ธ์ง€ํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.
static์ด๋ผ๋Š” ๋ช…๋ถ„ ๋•Œ๋ฌธ์— ๊ฐ์ฒด๋ฅผ ๊ณต์œ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์œผ๋‚˜,
์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด
์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค.

์™œ static์ด๋ผ๋Š” ๋ช…๋ชฉ ๋•Œ๋ฌธ์— ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฌด์กฐ๊ฑด
๊ณต์œ ๋˜๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ ๋งŒ ์ƒ๊ฐํ–ˆ์„๊นŒ ๐Ÿฅฒ

๋•Œ๋ฌธ์— ์ด๋ฒˆ ๋ฆฌํŒฉํ† ๋ง์—์„œ๋Š” ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ ๊ทน ์‚ฌ์šฉํ•ด ๋ณด์•˜๋‹ค.

๋งŽ์€ ์ฑ…์ž„์„ ์ง€๊ณ  ์žˆ๋˜ LottoController

์ด ๋‹น์‹œ ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ LottoController๋Š”

private LottoTickets buyLottoTickets(LottoCount lottoCount) {
    List<List<LottoNumber>> lottoNumbersGroup = new ArrayList<>();
    lottoNumbersGroup.addAll(createAllManualLottoTicket(lottoCount.getManualLottoCount()));
    lottoNumbersGroup.addAll(createAutoNumbers(lottoCount.getAutoLottoCount()));
    return new LottoTickets(lottoNumbersGroup);
}

์ด๋Ÿฐ์‹์œผ๋กœ LottoTicket์— ํ•ด๋‹นํ•˜๋Š” List<LottoNumber>๋„ Controller์—์„œ ๋งŒ๋“ค๊ณ ,
์ด๋ฅผ ๊ฐ€์ง€๊ณ  LottoTickets๊นŒ์ง€ ๋งŒ๋“ค์–ด ๋‚ธ๋‹ค.
๋˜ ๋ณด๋‹ค์‹ถ์ด List<List<LottoNumber>>๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๊ธฐ ์œ„ํ•ด ๋ณต์žกํ•œ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์—ˆ๋‹ค.

private List<List<LottoNumber>> createAllManualLottoTicket(int manualLottoCount) {
    OutputView.printInputManualLottoNumbers();
    return IntStream.range(0, manualLottoCount)
            .mapToObj(i -> InputView.inputNumbers()
                    .stream()
                    .map(input -> LottoNumber.valueOf(ParseUtil.parseInt(input)))
                    .collect(Collectors.toList()))
            .collect(Collectors.toList());
}

์ด์— ๋Œ€ํ•ด ๋‚จ๊ฒจ์ฃผ์‹  ํ”ผ๋“œ๋ฐฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์ด๋กœ ์ธํ•ด ์ „๋ฐ˜์ ์œผ๋กœ ๋งŽ์€ ์ˆ˜์ •์ด ์ผ์–ด๋‚ฌ๋‹ค.
LottoTicket์— auto(), manual() ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค๊ณ ,
LottoTickets์—๋„ auto() ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์–ด
LottoController์—์„œ ์ƒ์„ฑํ•˜๊ณ  ์žˆ๋˜ ์ฑ…์ž„์„ ๋ถ„๋ฐฐํ–ˆ๋‹ค.

LottoTicket - auto(), manual()

public static LottoTicket auto() {
    return new LottoTicket(LottoNumbersFactory.generateAutoLottoNumbers());
}

public static LottoTicket manual(List<String> lottoNumberStrings) {
    return lottoNumberStrings.stream()
            .map(i -> LottoNumber.valueOf(ParseUtil.parseInt(i)))
            .collect(collectingAndThen(toList(), LottoTicket::new));
}

LottoTicket์—์„œ List<String>์„ ๋ฐ›์•„ ์ˆ˜๋™ ๋กœ๋˜ ํ‹ฐ์ผ“์„ ์ƒ์„ฑํ•˜๋„๋ก ๋ฆฌํŒฉํ† ๋ง ํ•ด๋ณด์•˜๋‹ค !

LottoTickets - auto()

public static LottoTickets auto(int count) {
    return Stream.generate(LottoTicket::auto)
            .limit(count)
            .collect(collectingAndThen(toList(), LottoTickets::new));
}

๋•๋ถ„์— LottoContoller์— ์žˆ๋˜ ๋งŽ์€ ์ฑ…์ž„์ด ๋ถ„๋ฐฐ๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ (?), ๊ฐ ๋ฉ”์„œ๋“œ ์ฝ”๋“œ๋„ ๊ฐ„๊ฒฐํ•ด์กŒ๋‹ค๊ณ  ๋Š๋‚€๋‹ค.

LottoController ์ผ๋ถ€

private LottoTickets buyLottoTickets(LottoCount lottoCount) {
    LottoTickets lottoTickets = createManualLottoTickets(lottoCount.getManualCount());
    lottoTickets.combine(LottoTickets.auto(lottoCount.getAutoCount()));
    return lottoTickets;
}

private LottoTickets createManualLottoTickets(int manualCount) {
    OutputView.printInputManualLottoNumbers();
    return Stream.generate(() -> LottoTicket.manual(InputView.inputNumbers()))
                .limit(manualCount)
                .collect(collectingAndThen(toList(), LottoTickets::new));
}

LottoTickets

ํ”ผ๋“œ๋ฐฑ์„ ๋“ฃ๊ณ  ์ƒ๊ฐํ•ด๋ณด๋‹ˆ LottoTickets๋ฅผ ๋‚˜๋Š” ๋‹จ์ˆœํžˆ LottoTicket์˜ ๋ชจ์Œ์ด๋ผ๊ณ ๋งŒ ์ƒ๊ฐํ–ˆ์—ˆ๋‹ค.
(์•ฝ๊ฐ„ Repository๋กœ ์ƒ๊ฐํ–ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.)

๋ฆฌํŽ™ํ† ๋ง ์ „ LottoTickets

private final List<LottoTicket> lottoTickets;

public LottoTickets(List<List<LottoNumber>> lottoNumbersGroup) {
    this.lottoTickets = new ArrayList<>(createLottoTickets(lottoNumbersGroup));
}

public List<LottoTicket> lottoTickets() {
    return Collections.unmodifiableList(lottoTickets);
}

private List<LottoTicket> createLottoTickets(List<List<LottoNumber>> lottoNumbersGroup) {
    return lottoNumbersGroup.stream()
            .map(LottoTicket::new)
            .collect(Collectors.toList());
}

๊ณ ๋ฏผํ•ด๋ณด๋‹ˆ ์ด์ „์— ์•„๋ž˜์™€ ๊ฐ™์ด WinningTicket์—์„œ ๋‹น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ์‚ฐ์ถœํ–ˆ์—ˆ๋Š”๋ฐ,

๋ฆฌํŽ™ํ† ๋ง ์ „ WinnigLotto

public LottoResult checkPrizes(LottoTickets lottoTickets) {
    return lottoTickets.lottoTickets().stream()
            .map(this::matchPrize)
            .collect(collectingAndThen(toList(), LottoResult::new));
}

public Prize matchPrize(LottoTicket lottoTicket) {
    int matchCount = getMatchingCount(lottoTicket);
    boolean isBonusNumber = isContainBonusNumber(lottoTicket);
    return Prize.findPrize(matchCount, isBonusNumber);
}

private int getMatchingCount(LottoTicket lottoTicket) {
    return (int) lottoTicket.lottoTicket().stream()
                .filter(winningTicket.lottoTicket()::contains)
                .count();
}

    private boolean isContainBonusNumber(LottoTicket lottoTicket) {
    return lottoTicket.lottoTicket()
                .stream()
                .anyMatch(lottoNumber -> lottoNumber.equals(bonusNumber));
}

์ด๋ ‡๊ฒŒ ๋ชจ๋“  ๋ฉ”์„œ๋“œ์—์„œ LottoTicket์˜ ๊ฐ’์„ ๊บผ๋‚ด์˜ค๊ณ  ์žˆ์—ˆ๋‹ค.
๋•Œ๋ฌธ์— LottoTickets์— WinnigLotto๋ฅผ ์ฃผ์–ด ๊ฐ LottoTicket์—๊ฒŒ ๋น„๊ตํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๋Š”๊ฒƒ์ด ๋” ์˜ณ๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ
LottoTickets์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๋น„๊ตํ•˜๋Š” ์—ญํ• ์„ ๋ถ€์—ฌํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

๋˜ Controller์—์„œ ๋กœ๋˜ ํ‹ฐ์ผ“์„ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ๋œ๊ธฐ ์œ„ํ•ด ๋ฉ”์„œ๋“œ๋“ค์ด ๋ฐ”๋กœ LottoTickets๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜๋ฉด์„œ
ManualLottoTickets์™€ LottoTickets.auto๋ฅผ์ƒ์„ฑํ•˜๋Š”๋ฐ
์ƒ์„ฑ๋œ ์ด ๋‘ LottoTickets๋“ค์„ ํ•ฉ์น˜๋Š” ์—ญํ• ๋„ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ
combine()ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ WinnigLotto์— ์žˆ๋˜ ๊ฒƒ๋“ค์„ LottoTickets๋กœ ๋ถ„๋ฐฐํ•˜๋‹ˆ WinningLotto์˜
์—ญํ• ์ด ๋‹ค ๋นผ์•—๊ฒจ ๋ฒ„๋ฆฐ๊ฑด ์•„๋‹๊นŒ ๊ณ ๋ฏผ์ด ๋˜์—ˆ์ง€๋งŒ,

๋ฆฌ๋ทฐ์–ด ๋ถ„๊ป˜์„œ ๊ดœ์ฐฎ๋‹ค๊ณ  ๋ง์”€ํ•ด์ฃผ์…จ๋‹ค.

๋ฏธ์…˜์„ ์ง„ํ–‰ํ•˜๋ฉด ํ• ์ˆ˜๋ก ์•„์ง ๋ฐฐ์šธ ๊ฒƒ์ด ๋งŽ๊ณ  ๋‚˜์˜ ๋ถ€์กฑํ•จ์„ ๋Š๋‚€๋‹ค.
๋ฐฐ์šธ ๊ฒƒ์ด ๋งŽ๋‹ค๋Š” ๊ฒƒ์€ ๋Š๋‚„ ์„ฑ์ทจ๊ฐ์ด ๋งŽ๋‹ค๋Š” ๊ฒƒ์ด๋‹ˆ ์•ž์œผ๋กœ ํ™”์ดํŒ…ํ•˜์ž ๐Ÿƒโ€โ™‚๏ธ