본문 바로가기
kotlin

Lotto - 2. Lottos (일급 컬렉션) / Shop

by 쭈꾸마뇽 2021. 11. 9.

Lottos

public class Lottos {
    private final List<Lotto> lottos = new ArrayList<>();

    private final int cost;

    public Lottos(int count, List<List<Integer>> manualLottoNums, int cost) {
        this.cost = cost;
        for (int i = 0; i < count; i++) {
            lottos.add(Lotto.createLotto());
        }
        for (List<Integer> manualLottoNum : manualLottoNums) {
            lottos.add(Lotto.createLotto(manualLottoNum));
        }        
    }

    public static Lottos createLottos(int count, int cost) {
        return new Lottos(count, new ArrayList<>(), cost);
    }

    public static Lottos createLottos(int count, List<List<Integer>> manualLottoNums, int cost) {
        return new Lottos(count, manualLottoNums, cost);
    }

    public int size() {
        return lottos.size();
    }

    public List<Lotto> getLottos() {
        return lottos;
    }

    public int getTotalCost() {
        return cost;
    }
}
class Lottos(
    private val count: Int, manualLottoNumbers: List<List<Int>>
) {
    private val lottos: List<Lotto> = manualLottoNumbers.map { list -> Lotto(list.map { i -> Number(i) }) }
            .plus(Stream.generate { Lotto() }
                    .limit(countMinusManualSize(manualLottoNumbers))
                    .toList())

    init {
        require(totalCountOverTheManualSize(manualLottoNumbers)) {
            "수동 입력한 로또 번호의 갯수가 전체 구매 갯수보다 작거나 같아야합니다"
        }
    }

    private fun totalCountOverTheManualSize(manualLottoNumbers: List<List<Int>>) = 
    	countMinusManualSize(manualLottoNumbers) >= 0

    private fun countMinusManualSize(manualLottoNumbers: List<List<Int>>) = 
    	count.toLong() - manualLottoNumbers.size

    fun lottos() = lottos

    fun count() = count

    fun cost() = lottos.sumOf { lotto -> lotto.cost() }
}

Lottos는 Lotto의 리스트를 담는 일급컬렉션 클래스이자 Lotto구매시 입력값에 따라 Lotto를 초기화해누는 클래스이다.  일급컬렉션을 사용하게 되면 로또의 리스트로 직접 로직을 수행하지 않고 일급컬렉션 클래스에서 수행하게 된다.  일례로 cost() 함수에서는 Lotto들의 값의 함을 구하는 함수이다.  만약 일급컬렉션을 사용하지 않는다면 해당 함수를 호출하는 부분에서 직접 for문을 돌거나 같은 람다함수를 통해서 값을 구해야 한다.  일급컬렉션이 이 역할을 대신 해주며 로직단의 코드가 간결해진다.


Shop

public class Shop {
    public static final int LOTTO_PRICE = 1000;

    private Shop() {
    }

    public static Lottos buyLotto(int money) {
        validateMoney(money);

        int count = getLottoCount(money);

        return Lottos.createLottos(count, LOTTO_PRICE * count);
    }

    public static Lottos buyLotto(int money, List<List<Integer>> manualLottoNums) {
        validateMoney(money);

        int count = getLottoCount(money) - manualLottoNums.size();
        validateManualCount(count);

        return Lottos.createLottos(count, manualLottoNums, LOTTO_PRICE * count);
    }

    private static void validateMoney(int money) {
        if (isNotAvailableMoney(money)) {
            throw new RuntimeException("잘못된 금액을 입력하셨습니다.");
        }
    }

    private static boolean isNotAvailableMoney(int money) {
        return money < 0;
    }

    public static void validateManualCount(int count) {
        if (isLottoCountNotAvailable(count)) {
            throw new RuntimeException("구매한 금액보다 더 많은 수동 로또를 입력하였습니다.");
        }
    }

    private static boolean isLottoCountNotAvailable(int count) {
        return count < 0;
    }

    private static int getLottoCount(int money) {
        return money / LOTTO_PRICE;
    }
}
class Shop {
    companion object {
        private const val COST = 1000

        fun buyLotto(money: Int, manualLottoNumbers: List<List<Int>>): Lottos {
            require(moneyIsPositive(money)) {
                "잘못된 금액을 입력하셨습니다"
            }

            val count = calculateCount(money)
            check(moneyOverManualLottoCount(manualLottoNumbers, count)) {
                "구매한 금액보다 더 많은 수동 로또를 입력하였습니다"
            }

            return Lottos(count, manualLottoNumbers)
        }

        private fun moneyIsPositive(money: Int) =
            money >= 0

        private fun moneyOverManualLottoCount(manualLottoNumbers: List<List<Int>>, count: Int) =
            manualLottoNumbers.size <= count

        private fun calculateCount(money: Int) =
            money / COST
    }
}

Shop 클래스는 로또를 구매하는, Lottos를 생성하는 일종의 팩토리 클래스이다.  자바 코드와 코틀린 코드의 로직이 거의 동일한데 구현은 훨씬 간단한 것을 볼 수 있다.  가장 큰 차이점은 validation 로직 부분이다.  자바에서는 validate~ 함수를 빼서 입력값 검증을 하였다.  때문에 validate 함수와 if문에 들어가는 함수를 쌍으로 약간 복잡하게 구현이 되었는데 반면에 코틀린은 require와 check함수를 사용했다.  require와 check함수는 조건식을 만족하지 못하면  각각IllegalArgumentException과 IllegalStateException을 발생시킨다.  따라서 본인이 만들고자 하는 예외에 따라 선택해서 사용하면 된다.  이 코드에서는 두 함수를 buyLotto함수에 그대로 작성했는데 필요에 따라 함수로 또 빼서 구현할 수도 있다.


소스코드

 

GitHub - rkdals213/kotlin

Contribute to rkdals213/kotlin development by creating an account on GitHub.

github.com

'kotlin' 카테고리의 다른 글

Lotto - 1. Lotto  (0) 2021.11.07

댓글