[level1] 로또(자동) 1단계

유콩·2022년 3월 2일
0

우아한테크코스

목록 보기
4/15

우아한테크코스 레벨1 미션인 로또의 1단계 피드백 내용을 정리한다.

이번 로또 1단계 미션에서 신경썼던 것은 지난번 자동차 경주 구현 미션에서 새로 알게된 것들을 적응하게 될때까지 사용해보는 것이었다.

  • 일급 컬렉션
  • TDD
  • Stream
  • 불변 객체

생성자 파라미터


생성자 호출을 다른 개발자도 할 수 있다는 것을 간과하고 내가 개발하면서 편한 파라미터 타입으로 생성자를 만들었었다. 단순하게 구입 금액에 대한 값을 Input 에서 String 으로 받아오니까 Payment 생성자 파라미터는 String 으로 해야지~하고 만들었다.😔

당시엔 편할지몰라도 이후에 기능이 확장되고 생성자를 호출하는 곳이 늘어남에 따라 문제가 발생한다. 어떤 곳에는 Integer 로 갖고 있다가 호출하고 싶을 것이고 어떤 곳은 그대로 String 으로 받아올 수도 있다. 객체들을 담는 Collections 였다면 더더욱 경우의 수가 늘어난다. 프로그램이 확장될수록 생성자의 기준 이 어디인지 파악하기 어렵다.

결국 생성자 파라미터 타입은 인스턴스 변수 타입에 맞추는 것이 가장 적절하다고 생각한다. 파라미터 타입에 맞는 검증만을 필요로 하기 때문에 그 외에 타입에 대한 검증은 생성자 외부에서 진행하므로 생성자 내 검증 범위가 단순화된다. 또 파라미터 타입이 늘어나는 가정을 하지 않아도 되기 때문에 내부 로직에 대한 수정을 줄일 수 있다.

compareTo 의 용도


compareTo 라는 이름에 맞게 비교할 때 사용하는건가? 그럼 결과값으로 왜 int 를 받지? 하다가 두 개의 로또간 일치하는 로또 번호 개수를 반환하는데 사용했었다. 그러자 compareTo 는 이런 상황에 사용하는 것이 아니라는 듯한 피드백을 받고 다시 찾아봤다.

이펙티브 자바 아이템 14에 의하면, compareTo를 단순 동치성 비교에 더해 순서까지 비교할 수 있으며, 제네릭하다. Comparable을 구현했다는 것은 그 클래스의 인스턴스들에는 자연적인 순서(natural order)가 있음을 뜻한다. 라고 설명한다. 처음에는 대체 이게 무슨말인가 했는데 객체 간 정렬 기준을 정의하여 이후 정렬 시에 사용한다. 로또 번호인 Ball 에서 compareTo를 활용한다면 자동으로 생성한 로또들을 오름차순으로 출력하는 기능이 있으므로 오름차순으로 정렬하기 위해 Ball 의 숫자를 비교하여 더 큰값이 크다고 판단하도록 오버라이딩한다. 결국 매칭 개수를 구하는 로직에는 적합하지 않다.

클래스 생성 vs 인스턴스 변수 vs 메소드 내 변수


로또 미션 요구사항 중에 모든 원시 값과 문자열을 포장한다일급 컬렉션을 쓴다 가 있어 모든 값에 대해서 클래스로 감쌌다. 미션 진행하면서 객체지향의 사실과 오해를 읽고 객체로 생성해야 하는지, 인스턴스 변수로 생성해야 하는지, 아니면 메소드 내에서만 선언해도 되는지 정의내려봤다. (p. 48~52 참고)

클래스 생성

  • 값에 대한 로직(ex. 검증)이 필요한가?
  • 구현하는 프로그램 내 전반적으로 사용되는가?
  • 객체들과 상호작용이 있는가?

인스턴스 변수

  • 객체 내에서 여러 메소드에 걸쳐 사용되는가?
  • 다른 객체 간 상호작용할 때 필요한 값인가?

메소드 내 변수

  • 일회성인가?
  • 구현하는 프로그램에서 큰 의미를 가지고 있지 않은가?

위의 정의대로 판단해본다면 수익률은 구입 금액과 총 당첨 금액으로 계산하여 산출해낸 값이다. 다른 클래스 내에서 필요한 값이 아닌 마지막 결과에 포함된 값이므로 현재 상황에서는 객체가 없어도 된다고 생각하여 삭제하였다.

VO(Value Object)


로또 미션에서 로또 번호는 1에서 45사이의 값으로 고정된다. 로또는 로또 번호들이 일치해도 각각의 로또마다 유의미한 객체지만 로또 번호는 1은 1이다. 또 다른 로또 번호 1을 생성한다해도 이전에 생성한 객체와 구분하는 것이 의미가 없다. 로또 번호들이 일치할 경우 동일한 객체로 판단하여 처리하도록 구성하였다.

인스턴스 캐싱


로또 번호를 VO로 만들었고 사용되는 번호는 1에서 45사이로 항상 고정되어 있다. 매번 45개의 로또 번호 묶음을 생성하고 랜덤으로 섞어 6개를 뽑는 과정이 반복된다는 의미이다. 로또 구매 개수가 크지 않다면 상관없으나 개수가 늘어남에 따라 불필요한 과정들이 계속해서 반복된다. 동일한 과정이 반복되는 것을 막기 위해 45개의 로또 번호들을 클래스 내에 캐싱하여 호출하는 형태로 수정하였다. 45개의 번호 외에 다른 값을 추가하지 않도록 수정불가하게 반환한다.


public class Ball {
    public static final List<Ball> cacheBalls = new ArrayList<>();

    private final int number;

    static {
        cacheBalls.addAll(IntStream.range(MINIMUM_NUMBER, MAXIMUM_NUMBER + 1)
                .mapToObj(Ball::new)
                .collect(Collectors.toList()));
    }
    
    // 생략

    public static List<Ball> getTotalBalls() {
        return Collections.unmodifiableList(cacheBalls);
    }

참고) 테코블 - 반복적으로 사용되는 인스턴스 캐싱하기

2단계 목표

객체지향의 사실과 오해를 읽을수록 설계가 쉬워지고 재밌어진다. 2단계에서는 기능이 더 추가되는데 꾸준히 읽으면서 클래스의 역할이 적절하게 분배된 구조로 수정해야겠다.😁

2개의 댓글

comment-user-thumbnail
2022년 3월 2일

유콩 안녕하세요~
우리 모두 화이팅입니다!! 🎶

1개의 답글