[우아한 테크코스-프리코스 1주차] 숫자야구게임 API 내부 리뷰

우아한 테크코스 1주차 미션 숫자 야구 게임 GitHub Link

프로그래밍 요구 사항 - 제공 API 사용


프로그래밍 요구사항


프로그래밍 요구 사항에 사용해야하는 API 두 가지가 있다. 코드 내에서 Ctrl+B를 누르면 내부 함수로 타고 들어갈 수 있다. 이제 이 API를 활용하기 위해 내부 코드를 탐색해보자!



1. camp.nextstep.edu.missionutils.Console


package camp.nextstep.edu.missionutils;

import java.util.Scanner;

public class Console {
    private static Scanner scanner;

    private Console() {
    }

    public static String readLine() {
        return getInstance().nextLine();
    }

    public static void close() {
        if (scanner != null) {
            scanner.close();
            scanner = null;
        }
    }

    private static Scanner getInstance() {
        if (scanner == null) {
            scanner = new Scanner(System.in);
        }
        return scanner;
    }
}

readLine()

평소에 입력 기능을 위해 자주 사용하던 Scanner를 왜 다시 감싸줬을까? 뭐가 달라졌을까? 를 고민하다가 그럼 요구사항대로 하지 않았을 때 어떤 상황을 방지하려고 이 요구사항을 추가했을까? 를 생각해보았다. 결론은 Scanner 개체 생성 남발을 방지하고 싱글톤 패턴을 위해서 라고 판단했다.

2. camp.nextstep.edu.missionutils.Randoms


package camp.nextstep.edu.missionutils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

public class Randoms {
    private static final Random defaultRandom = ThreadLocalRandom.current();

    private Randoms() {
    }

    public static int pickNumberInList(final List<Integer> numbers) {
        validateNumbers(numbers);
        return numbers.get(pickNumberInRange(0, numbers.size() - 1));
    }

    public static int pickNumberInRange(final int startInclusive, final int endInclusive) {
        validateRange(startInclusive, endInclusive);
        return startInclusive + defaultRandom.nextInt(endInclusive - startInclusive + 1);
    }

    public static List<Integer> pickUniqueNumbersInRange(
        final int startInclusive,
        final int endInclusive,
        final int count
    ) {
        validateRange(startInclusive, endInclusive);
        validateCount(startInclusive, endInclusive, count);
        final List<Integer> numbers = new ArrayList<>();
        for (int i = startInclusive; i <= endInclusive; i++) {
            numbers.add(i);
        }
        return shuffle(numbers).subList(0, count);
    }

    public static <T> List<T> shuffle(final List<T> list) {
        final List<T> result = new ArrayList<>(list);
        Collections.shuffle(result);
        return result;
    }

    private static void validateNumbers(final List<Integer> numbers) {
        if (numbers.isEmpty()) {
            throw new IllegalArgumentException("numbers cannot be empty.");
        }
    }

    private static void validateRange(final int startInclusive, final int endInclusive) {
        if (startInclusive > endInclusive) {
            throw new IllegalArgumentException("startInclusive cannot be greater than endInclusive.");
        }
        if (endInclusive == Integer.MAX_VALUE) {
            throw new IllegalArgumentException("endInclusive cannot be greater than Integer.MAX_VALUE.");
        }
        if (endInclusive - startInclusive >= Integer.MAX_VALUE) {
            throw new IllegalArgumentException("the input range is too large.");
        }
    }

    private static void validateCount(final int startInclusive, final int endInclusive, final int count) {
        if (count < 0) {
            throw new IllegalArgumentException("count cannot be less than zero.");
        }
        if (endInclusive - startInclusive + 1 < count) {
            throw new IllegalArgumentException("count cannot be greater than the input range.");
        }
    }
}

pickNumberInRange()

먼저 내가 사용해야 할 pickNumberInRange() 는 이름에서도 알 수 있듯이 범위 내의 랜덤한 수를 선택한다는 것을 알 수 있다. 코드 내부를 보면 validateRange()로 매개변수를 다시 전달하는 것을 알 수 있는데, 이 함수는 지정한 범위가 유효한 지를 검사하는 함수인 것을 알 수 있다. 그리고 다시 돌아가서 defaultRandom.nextInt()함수를 사용한다. 이 함수는 java.util.concurrent.ThreadLocalRandom를 타고 들어가보면 시드 값 초기화를 할 필요 없이 정수인 난수를 생성한다.




[참고자료]