본문 바로가기
카테고리 없음

Lotto - 3. 등수 매기기

by 쭈꾸마뇽 2021. 11. 9.

LastWeekLotto

public class LastWeekLotto {
    private final Lotto lastWeekLottoNum;

    private final int bonusNum;

    protected LastWeekLotto(List<Integer> lastWeekLottoNum, int bonusNum) {
        this.lastWeekLottoNum = Lotto.createLotto(lastWeekLottoNum);
        this.bonusNum = bonusNum;
    }

    public static LastWeekLotto of(List<Integer> lastWeekLottoNum, int bonusNum) {
        validateBonusNum(bonusNum);
        return new LastWeekLotto(lastWeekLottoNum, bonusNum);
    }

    public boolean contains(int num) {
        return this.lastWeekLottoNum.getLottoNums().contains(num);
    }

    public boolean isBonusNumCorrect(int num) {
        return this.bonusNum == num;
    }

    private static void validateBonusNum(int bonusNum) {
        if (notInRange(bonusNum)) {
            throw new RuntimeException("로또 번호는 1 ~ 45 사이의 번호여야 합니다");
        }
    }

    private static boolean notInRange(int num) {
        return num > 45 || num <= 0;
    }
}
class LastWeekLotto(
    private val lottoNumbers: List<Number>,
    private val bonusNumber: Number
) {
    companion object {
        private const val LOTTO_NUMBER_COUNT = 6
    }

    init {
        require(lottoNumbers.distinct().size == LOTTO_NUMBER_COUNT) {
            "로또 번호의 갯수는 중복되지 않고 6개여야 합니다"
        }
    }

    fun contains(number: Number) =
        lottoNumbers.contains(number)

    fun isBonusNumberMatch(number: Number) =
        bonusNumber == number

}

LastWeekLotto 클래스는 당첨 로또 번호를 표현하는 클래스이다.  자바와 코틀린 코드 구현상 차이는 없고 공통적으로 

로또 번호가 일치하는지와 보너스 번호가 일치하는지 판별하는 함수를 가지고 있다.  그리고 코틀린 코드에서는 Number 클래스에 로또 번호 검증 로직을 작성하였기 때문에 검증 로직이 자바 코드에 적은 모습만 보인다.


LottoPlace

public enum LottoPlace {
    FIRST(2_000_000_000, 6, false),
    SECOND(30_000_000, 5, true),
    THIRD(1_500_000, 5, false),
    FORTH(50_000, 4, false),
    FIFTH(5_000, 3, false),
    NONE(0, 0, false);

    private final int price;
    private final int correctNum;
    private final boolean isBonusNumCorrect;

    LottoPlace(int price, int correctNum, boolean isBonusNumCorrect) {
        this.price = price;
        this.correctNum = correctNum;
        this.isBonusNumCorrect = isBonusNumCorrect;
    }

    public static LottoPlace findPlaceByCorrectNum(int correctNum, boolean isBonusNumCorrect) {
        return Arrays.stream(values())
            .filter(i -> i.correctNum == correctNum && (isBonusNumCorrect & i.isBonusNumCorrect) == i.isBonusNumCorrect)
            .findFirst()
            .orElse(LottoPlace.NONE);
    }

    public int getPrice() {
        return price;
    }

    public int getCorrectNum() {
        return correctNum;
    }
}
enum class LottoPlace(
    private val correctNumberCount: Int,
    private val bonusNumberCorrect: Boolean,
    private val prize: Int
) {
    FIRST(6, false, 2_000_000_000),
    SECOND(5, true, 30_000_000),
    THIRD(5, false, 1_500_000),
    FORTH(4, false, 50_000),
    FIFTH(3, false, 5_000),
    NOTHING(0, false, 0);

    companion object {
        fun findByCorrectNumberCount(count: Int, isBonusNumberCorrect: Boolean) = values().find { lottoPlace ->
            correctNumberCountEqual(lottoPlace, count) && isBonusNumberCorrect(lottoPlace, isBonusNumberCorrect)
        } ?: NOTHING


        private fun correctNumberCountEqual(lottoPlace: LottoPlace, count: Int) =
            lottoPlace.correctNumberCount == count

        private fun isBonusNumberCorrect(lottoPlace: LottoPlace, isBonusNumberCorrect: Boolean) =
            (lottoPlace.bonusNumberCorrect && isBonusNumberCorrect) == lottoPlace.bonusNumberCorrect

        fun valuesWithoutNothing(): List<LottoPlace> = values()
            .asList()
            .subList(0, 5)
    }

    fun prize(): Int = prize

    fun correctCount(): Int = correctNumberCount

    fun bonusNumberCorrect(): Boolean = bonusNumberCorrect
}

로또 등수를 나타내는 LottoPlace 클래스도 큰 차이는 없다.  다만 람다 사용에서 코틀린이 좀 더 간략하였고 등수가 없는 경우에 등수없을을 나타내는 Enum을 리턴하기 위해 자바에서는 orElse()를 사용했고 코틀린은 ?: 문법을 사용했다.


LottoPlaceChecker

public class LottoPlaceChecker {
    private final LastWeekLotto lastWeekLotto;

    private LottoPlaceChecker(LastWeekLotto lastWeekLotto) {
        this.lastWeekLotto = lastWeekLotto;
    }

    public static LottoPlaceChecker of(LastWeekLotto lastWeekLotto) {
        return new LottoPlaceChecker(lastWeekLotto);
    }

    private static long calculateTotalPrice(List<LottoPlace> lottoPlaces) {
        return lottoPlaces.stream()
            .mapToLong(LottoPlace::getPrice)
            .sum();
    }

    public List<LottoPlace> getLottoPlace(Lottos lottos) {
        List<LottoPlace> results = new ArrayList<>();
        for (Lotto lotto : lottos.getLottos()) {
            int correctNum = lotto.countCorrectNums(lastWeekLotto);
            boolean isBonusNumCorrect = lotto.isLottoNumContainsBonusNum(lastWeekLotto);
            results.add(LottoPlace.findPlaceByCorrectNum(correctNum, isBonusNumCorrect));
        }
        return results;
    }

    public BigDecimal calculateWinnerRate(List<LottoPlace> lottoPlaces, int totalCost) {
        long sum = calculateTotalPrice(lottoPlaces);
        return BigDecimal.valueOf(sum)
            .divide(BigDecimal.valueOf(totalCost), 6, RoundingMode.HALF_EVEN);
    }
}
class LottoPlaceChecker(
    private val lastWeekLotto: LastWeekLotto
) {
    fun getLottoPlaces(lottos: Lottos): List<LottoPlace> {
        return lottos.lottos().map { lotto -> getLottoPlace(lotto) }
    }

    private fun getLottoPlace(lotto: Lotto): LottoPlace {
        val countMatchNumbers = lotto.countMatchNumbers(lastWeekLotto)
        val bonusNumberCorrect = lotto.isBonusNumberCorrect(lastWeekLotto)
        return LottoPlace.findByCorrectNumberCount(countMatchNumbers, bonusNumberCorrect)
    }
}

LottoPlaceChecker 클래스는 로또 등수를 판별하는 클래스이다.  getLottoPlace 함수를 통해 구매한 로또의 등수를 판별하는데 약간의 차이가 있다.  자바코드에서는 Lottos의 Lotto 리스트를 가져와서 for문을 돌리며 등수를 판별해 일일이 결과 리스트에 넣어주었는데, 코틀린 코드에서는 람다함수의 map을 이용해서 풀어냈다.  확실히 for문보다는 람다함수를 이용하는게 간략하고 코드 해석이 용이하다. 

또 하나의 차이점이라면 로또 수익률을 계산하는 함수인데, 자바 코드로 구현할 때는 LottoPlaceChecker 클래스에서 수익률을 계산했다.  하지만 코틀린 코드에서는 출력담당 클래스로 해당 기능을 넘겼는데 지금 생각하기로는 LottoPlaceChecker 클래스는 로또 등수를 판별하는 클래스이지 돈을 계산하는 클래스가 아니다.  그렇기에 기존 자바 코드의 구현은 클래스가 너무 많은 일을 한다고 생각이 들어서 해당 기능을 분류하였다.


소스코드

 

GitHub - rkdals213/kotlin

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

github.com

 

댓글