๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ’ป/Java CleanCode Study

๐ŸŽฐ๋กœ๋˜ ๊ฒŒ์ž„ (MVC ํŒจํ„ด) #2022.02.21~2022.03.05

๐Ÿš€ ๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ

  • ๋กœ๋˜ ๊ฒŒ์ž„ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค. ๊ทœ์น™์„ ๋ชจ๋ฅด๋ฉด ๊ฒ€์ƒ‰ํ•ด ๋กœ๋˜ ๊ทœ์น™์„ ์ฐพ์•„๋ณธ๋‹ค.
  • ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก์„ ์ž…๋ ฅํ•˜๋ฉด ๊ตฌ์ž… ๊ธˆ์•ก์— ํ•ด๋‹นํ•˜๋Š” ๋กœ๋˜๋ฅผ ๋ฐœ๊ธ‰ํ•ด์•ผ ํ•œ๋‹ค.
  • ๋กœ๋˜ 1์žฅ์˜ ๊ฐ€๊ฒฉ์€ 1000์›์ด๋‹ค.
  • ๋กœ๋˜ ๋‹น์ฒจ ๊ธˆ์•ก์€ ๊ณ ์ •๋˜์–ด ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•œ๋‹ค.
  • ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•ด ์ถœ๋ ฅํ•ด์•ผ ํ•œ๋‹ค.

 

โœ๐Ÿป ์ž…์ถœ๋ ฅ ์š”๊ตฌ์‚ฌํ•ญ

โŒจ๏ธ ์ž…๋ ฅ

  • ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก
  • ๋กœ๋˜ ๋‹น์ฒจ๋ฒˆํ˜ธ
  • ๋กœ๋˜ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ

๐Ÿ–ฅ ์ถœ๋ ฅ

  • ๊ตฌ์ž…ํ•œ ๋กœ๋˜ ๊ฐœ์ˆ˜๋งŒํผ ์ƒ์„ฑ๋œ ๋กœ๋˜๋ฒˆํ˜ธ ๋ชฉ๋ก
3๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.
[8, 21, 23, 41, 42, 43]
[1, 3, 6, 7, 33, 37]
[6, 16, 25, 29, 39, 45]
  • ๋‹น์ฒจ ํ†ต๊ณ„
๋‹น์ฒจ ํ†ต๊ณ„
--------------
3๊ฐœ ์ผ์น˜ (5000์›) - 1๊ฐœ
4๊ฐœ ์ผ์น˜ (50000์›) - 0๊ฐœ
5๊ฐœ ์ผ์น˜ (1500000์›) - 0๊ฐœ
5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30000000์›) - 0๊ฐœ
์ด ์ˆ˜์ต๋ฅ ์€ 1.66์ž…๋‹ˆ๋‹ค.

๐Ÿ’ป ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์‹คํ–‰ ๊ฒฐ๊ณผ ์˜ˆ์‹œ

๊ตฌ์ž…๊ธˆ์•ก์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.
3000

3๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.
[8, 21, 23, 41, 42, 43]
[1, 3, 6, 7, 33, 37]
[6, 16, 25, 29, 39, 45]

์ง€๋‚œ ์ฃผ ๋‹น์ฒจ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.
1,2,3,4,5,6
๋ณด๋„ˆ์Šค ๋ณผ์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.
7

๋‹น์ฒจ ํ†ต๊ณ„
--------------
3๊ฐœ ์ผ์น˜ (5000์›) - 1๊ฐœ
4๊ฐœ ์ผ์น˜ (50000์›) - 0๊ฐœ
5๊ฐœ ์ผ์น˜ (1500000์›) - 0๊ฐœ
5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30000000์›) - 0๊ฐœ
์ด ์ˆ˜์ต๋ฅ ์€ 1.66์ž…๋‹ˆ๋‹ค.
… 

 

### MVC ํŒจํ„ด ๋ถ„๋ฆฌ 

###

- ๊ฐ™์€ ํด๋ž˜์Šค ๋‚ด์—์„œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ๋Š” private์œผ๋กœ ์„ ์–ธ

- controller๊ฐ€ view ์˜์—ญ์—๊ฒŒ ์š”์ฒญํ•ด์„œ ๋ฐ›์€ ๊ฐ’์„ model์˜์—ญ์œผ๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐฉ์‹

 

main controller

public class MainController {
    public void play() {
        List<Lotto> userLotto = createUserLotto(); // ์‚ฌ์šฉ์ž ๋กœ๋˜ ์ƒ์„ฑ
        WinningLotto winningLotto = createWinningLotto(); // ๋‹น์ฒจ ๋กœ๋˜ ์ƒ์„ฑ
        printLottoGameResult(userLotto, winningLotto); // ๋กœ๋˜ ๊ฒŒ์ž„ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
    }

    // ์‚ฌ์šฉ์ž ๋กœ๋˜ ์ƒ์„ฑ
    private List<Lotto> createUserLotto() {
        int cost = InputController.inputCost(); // view ํ˜ธ์ถœ, ์‚ฌ์šฉ์ž ๋กœ๋˜ ๊ตฌ์ž… ๊ธˆ์•ก ์ž…๋ ฅ
        int count = User.getCount(cost); // model ํ˜ธ์ถœ, ์‚ฌ์šฉ์ž ๋กœ๋˜ ๊ตฌ์ž… ๊ฐฏ์ˆ˜ ๊ตฌํ•˜๊ธฐ
        OutputView.printPurchaseMessage(count); // view ํ˜ธ์ถœ, ์‚ฌ์šฉ์ž ๋กœ๋˜ ๊ตฌ์ž… ๊ฐฏ์ˆ˜ ์•ˆ๋‚ด ๋ฉ”์„ธ์ง€ ์ถœ๋ ฅ

        List<Lotto> userLotto = User.getTotalLotto(count); // model ํ˜ธ์ถœ, ์‚ฌ์šฉ์ž ๊ตฌ์ž… ๋กœ๋˜ ๋ฆฌ์ŠคํŠธ ๊ตฌํ•˜๊ธฐ
        OutputView.printUserLotto(userLotto); // view ํ˜ธ์ถœ, ์‚ฌ์šฉ์ž ๊ตฌ์ž… ๋กœ๋˜ ๋ฆฌ์ŠคํŠธ ์ถœ๋ ฅ

        return userLotto;
    }

    // ๋‹น์ฒจ ๋กœ๋˜ ์ƒ์„ฑ
    private WinningLotto createWinningLotto() {
        String[] winningNumber = InputController.inputWinningNumber(); // view ํ˜ธ์ถœ, ์ง€๋‚œ์ฃผ ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ
        int bonusNumber = InputController.inputBonusNumber(); // view ํ˜ธ์ถœ, ์ง€๋‚œ์ฃผ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ž…๋ ฅ

        List<Integer> winningNumbers = Convert.changeStringArrayToList(winningNumber); // string[] > list<Integer>
        Lotto lotto = Lotto.getLotto(winningNumbers); // model ํ˜ธ์ถœ, ๋‹น์ฒจ ๋กœ๋˜ ์ƒ์„ฑ

        return new WinningLotto(lotto, bonusNumber);
    }

    // ๋กœ๋˜ ๊ฒŒ์ž„ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
    private void printLottoGameResult(List<Lotto> userLotto, WinningLotto winningLotto) {
        Map<Rank, Integer> statistics = Statistics.getWinningStatistics(userLotto, winningLotto); // model ํ˜ธ์ถœ, ๋‹น์ฒจ ํ†ต๊ณ„ ๊ตฌํ•˜๊ธฐ
        OutputView.printWinningStatistics(statistics); // view ํ˜ธ์ถœ, ๋‹น์ฒจ ํ†ต๊ณ„ ์ถœ๋ ฅ

        int winningMoney = Statistics.getTotalWinningMoney(statistics); // model ํ˜ธ์ถœ, ๋‹น์ฒจ ๊ธˆ์•ก ๊ตฌํ•˜๊ธฐ
        double yield = Statistics.getTotalYield(winningMoney, userLotto.size()); // model ํ˜ธ์ถœ, ์ด ์ˆ˜์ต๋ฅ  ๊ตฌํ•˜๊ธฐ
        OutputView.printTotalYield(yield); // view ํ˜ธ์ถœ, ์ด ์ˆ˜์ต๋ฅ  ์ถœ๋ ฅ
    }
}

model.Lotto

public class Lotto {
    private final List<Integer> numbers;

    public Lotto(List<Integer> numbers) {
        this.numbers = numbers;
    }

    // ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์€ ์ˆซ์ž๋กœ ์ƒ์„ฑ๋œ ๋กœ๋˜ ๋ฐ˜ํ™˜
    public static Lotto getLotto(List<Integer> numbers) {
        return new Lotto(numbers);
    }

    public List<Integer> getNumber() {
        return this.numbers;
    }

    // ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์‚ฌ์šฉ์ž ๋กœ๋˜ ๋ฒˆํ˜ธ ์ค‘ ์ผ์น˜ํ•˜๋Š” ๋ฒˆํ˜ธ๊ฐ€ ๋ช‡๊ฐœ์ธ์ง€ ๋ฐ˜ํ™˜
    public int getMatchCount(Lotto winningLotto) {
        List<Integer> user = numbers;
        List<Integer> winning = winningLotto.getNumber();
        List<Integer> win = new ArrayList<>();

        for (Integer i : winning) {
            win.add(Integer.valueOf(String.valueOf(i)));
        }

        user.retainAll(win);

        return user.size();
    }

    // ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š”์ง€ true/false ๋ฐ˜ํ™˜
    public boolean isContainsBonusNumber(int bonusNo) {
        return numbers.contains(bonusNo);
    }
}

model.Rank

public enum Rank {
	FIRST(6, 2_000_000_000), // 1๋“ฑ
	SECOND(5, 30_000_000), // 2๋“ฑ
	THIRD(5, 1_500_000), // 3๋“ฑ
	FOURTH(4, 50_000), // 4๋“ฑ
	FIFTH(3, 5_000), // 5๋“ฑ
	MISS(0, 0);

	private static final int WINNING_MIN_COUNT = 3;

	private int countOfMatch;
	private int winningMoney;

	private Rank(int countOfMatch, int winningMoney) {
		this.countOfMatch = countOfMatch;
		this.winningMoney = winningMoney;
	}

	public int getCountOfMatch() {
		return countOfMatch;
	}

	public int getWinningMoney() {
		return winningMoney;
	}

	public static Rank valueOf(int countOfMatch, boolean matchBonus) {
		if (countOfMatch < WINNING_MIN_COUNT) {
			return MISS;
		}

		if (SECOND.matchCount(countOfMatch) && matchBonus) {
			return SECOND;
		}

		for (Rank rank : values()) {
			if (rank.matchCount(countOfMatch) && rank != SECOND) {
				return rank;
			}
		}

		throw new IllegalArgumentException(countOfMatch + "๋Š” ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฐ’์ž…๋‹ˆ๋‹ค.");
	}

	private boolean matchCount(int countOfMatch) {
		return this.countOfMatch == countOfMatch;
	}
}

model.Statistics

public class Statistics {
    private static final int LOTTO_TICKET_PRICE = 1000;

    // ๋‹น์ฒจ ํ†ต๊ณ„ ๊ตฌํ•˜๊ธฐ
    public static Map<Rank, Integer> getWinningStatistics(List<Lotto> userLotto, WinningLotto winningLotto) {
        Map<Rank, Integer> statistics = initStatistics();
        registerMatchStatistics(userLotto, winningLotto, statistics);

        return statistics;
    }

    // ์ด ๋‹น์ฒจ ๊ธˆ์•ก ๋ฐ˜ํ™˜
    public static int getTotalWinningMoney(Map<Rank, Integer> statistics) {
        int totalWinningMoney = 0;

        for (Rank rank : Rank.values()) {
            int winningMoney = rank.getWinningMoney();
            int amount = statistics.get(rank);

            totalWinningMoney += winningMoney * amount;
        }

        return totalWinningMoney;
    }

    // ์ด ์ˆ˜์ต๋ฅ  ๋ฐ˜ํ™˜
    public static double getTotalYield(double winningMoney, int amount) {
        double amountPaid = (amount * LOTTO_TICKET_PRICE);

        return winningMoney / amountPaid;
    }

    // ํ†ต๊ณ„ ์ดˆ๊ธฐํ™”
    private static Map<Rank, Integer> initStatistics() {
        Map<Rank, Integer> statistics = new HashMap<>();

        for (Rank rank : Rank.values()) {
            statistics.put(rank, 0);
        }

        return statistics;
    }

    // ๋งค์น˜ ํ†ต๊ณ„ ๊ฒฐ๊ณผ ๋“ฑ๋ก
    private static void registerMatchStatistics(List<Lotto> userLotto, WinningLotto winningLotto, Map<Rank, Integer> statistics) {
        for (Lotto lotto : userLotto) {
            Rank rank = winningLotto.match(lotto);
            statistics.put(rank, statistics.getOrDefault(rank, 0) + 1);
        }
    }
}

model.User

public class User {
    private static final int LOTTO_TICKET_PRICE = 1000;

    // ์‚ฌ์šฉ์ž ๋žœ๋ค ๋กœ๋˜ ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜
    public static List<Lotto> getTotalLotto(int count) {
        List<Lotto> userLotto = new ArrayList<>();

        // ๋žœ๋ค ๋กœ๋˜ ์ƒ์„ฑ
        for (int i = 0; i < count; i++) {
            Lotto lotto = RandomLottoNumber.getRandomLotto();
            userLotto.add(lotto);
        }

        return userLotto;
    }

    // ๋กœ๋˜ ๊ตฌ์ž… ๊ฐฏ์ˆ˜ ๊ณ„์‚ฐ ๋ฉ”์†Œ๋“œ
    public static int getCount(int cost) {
        return cost / LOTTO_TICKET_PRICE;
    }
}

model.Validator

public class Validator {
    private static final String ERROR_COST_MESSAGE = "[ERROR] ๋กœ๋˜ ํ•œ์žฅ ๋‹น 1000์› ์ž…๋‹ˆ๋‹ค.\n1000์› ์ด์ƒ, 1000์› ๋‹จ์œ„๋กœ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. \n";
    private static final String ERROR_BONUS_NUMBER_MESSAGE = "[ERROR] 1~45 ์‚ฌ์ด์˜ ๋ณด๋„ˆ์Šค ์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. \n";
    private static final String ERROR_WINNING_NUMBER_LENGTH_MESSAGE = "[ERROR] 6๊ฐœ์˜ ๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”. \n";
    private static final int MIN_LOTTO_COST = 1000;
    private static final int MIN_RANDOM_NUMBER = 1;
    private static final int MAX_RANDOM_NUMBER = 45;
    private static final int LOTTO_COST_REMAINDER = 0;

    // ๋ณด๋„ˆ์Šค ๋„˜๋ฒ„_๋ฒ”์œ„_1~45_์ฒดํฌ
    public static boolean isValidBonusNumber(int bonusNumber) {
        boolean check = true;

        if (bonusNumber < MIN_RANDOM_NUMBER || bonusNumber > MAX_RANDOM_NUMBER) {
            System.out.println(ERROR_BONUS_NUMBER_MESSAGE);
            check = false;
        }

        return check;
    }

    // ๋‹น์ฒจ ๋ฒˆํ˜ธ split ์ตœ๋Œ€ 6๊ฐœ
    public static boolean isValidNumberLength(String[] numbers) {
        boolean check = true;

        if (numbers.length != 6) {
            System.out.println(ERROR_WINNING_NUMBER_LENGTH_MESSAGE);
            check = false;
        }

        return check;
    }

    // ๊ตฌ์ž… ๊ธˆ์•ก ์ฒดํฌ
    public static boolean isValidCost(int cost) {
        boolean check = true;

        if (!(isValidCostMinCost(cost)) || !(isValidCostUnit(cost))) {
            System.out.println(ERROR_COST_MESSAGE);
            check = false;
        }

        return check;
    }

    // ๊ตฌ์ž… ๊ธˆ์•ก_๋‹จ์œ„_1000์› ๋‹จ์œ„์ธ์ง€
    private static boolean isValidCostUnit(int cost) {
        return ((cost % MIN_LOTTO_COST) == LOTTO_COST_REMAINDER);
    }

    // ๊ตฌ์ž… ๊ธˆ์•ก_์ตœ์†Œ๊ธˆ์•ก_1000์›์ธ์ง€
    private static boolean isValidCostMinCost(int cost) {
        return cost >= MIN_LOTTO_COST;
    }
}

model.WinningLotto

public class WinningLotto {
    private final Lotto lotto;
    private final int bonusNo;

    public WinningLotto(Lotto lotto, int bonusNo) {
        this.lotto = lotto;
        this.bonusNo = bonusNo;
    }

    public Rank match(Lotto userLotto) {
        int matchCount = userLotto.getMatchCount(lotto);
        boolean matchBonus = userLotto.isContainsBonusNumber(bonusNo);

        return Rank.valueOf(matchCount, matchBonus);
    }
}