원시 값 포장과 일급 컬렉션

  1. 로또 미션 요구사항 中 …
  2. 원시 값 포장?
  3. 일급 컬렉션?
  4. 참고자료

로또 미션 요구사항 中 …

  1. 모든 원시 값과 문자열을 포장한다.
  2. 일급 컬렉션을 쓴다.

원시 값 포장?

‘모든 원시 값과 문자열을 포장하라’라는 객체지향 체조 원칙에 나온 규칙 중 하나라고 한다.

원시값 포장은 Primitive Obsession Anti Pattern(도메인의 객체를 나타내기 위해 primitive타입을 쓰는 나쁜 습관)을 피하기 위해 필요하다.

즉, 원시값 포장은 원시 유형의 값(변수명)을 이용해 의미를 나타내지 않고, 의미있는 객체로 포장한다는 개념이라 볼 수 있다.

의미있는 객체란 무엇일까?

이번 로또 미션을 진행하며 LottoNumber를 검증해야하는 일이 생겼다.

만약, 포장하지 않았다면 어떻게 코드를 구성했을까?

// 1~45사이의 수를 랜덤화하고, 0번째 인덱스를 lottoNumber에 넣는다
val lottoNumber: Int = (1 .. 45).shuffled()[0] 

코드로만 봐선 lottoNumber에는 1 ~ 45 사이의 수가 들어갈 것 같다. 근데 만약 lottoNumber가 var로 생성이 되어 수정이 가능하다면? 그에 따른 검증, 값이 1~45만 들어간다는 보장이 될 수 있는가? 라는 의문이 들게 된다.

그럼 이 원시 값을 포장하는 객체로 만들게 되면 어떻게 될까?

class LottoNumber(val number: Int) {
    init {
        require(number in MINIMUM_LOTTO_NUMBER..MAXIMUM_LOTTO_NUMBER) { ERROR_MESSAGE_LOTTO_RANGE_1_TO_45 }
    }

    companion object {
        const val MINIMUM_LOTTO_NUMBER = 1
        const val MAXIMUM_LOTTO_NUMBER = 45
        const val ERROR_MESSAGE_LOTTO_RANGE_1_TO_45 = "로또 번호는 1에서 45 사이의 숫자여야 합니다"
    }
}

이와 같이 LottoNumber를 사용하게 되면, 1에서 45 사이의 수만을 갖는다는 것을 보장할 수 있다. 뿐만 아니라, 이러한 LottoNumber 6개를 갖는 Set<LottoNumber>(혹은 Lotto)는 매번 해당 번호가 1~45 사이의 수인지 검증하는 로직을 구성하지 않아도 되어 코드 유지 보수에 도움이 된다.

이러한 원시 값 포장에 대한 이점은 다음과 같이 정리할 수 있다.

  • 자신의 상태를 객체 스스로 관리할 수 있다.
  • 코드의 유지 보수에 도움이 된다.
  • 자료형에 구애받지 않는다. (여러 타입의 지원이 가능하다.)

일급 컬렉션?

일급 컬렉션이란 단어는 소트웍스 앤솔로지 의 객체지향 생활체조 파트에서 언급 되었다.

일급 컬렉션은 Collection을 Wrapping하면서, Wrapping한 Collection 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라 한다.

무슨 말이죠…?

위에서 LottoNumber라는 원시 값 포장 객체를 생성했다. 그러면 자연스럽게 로또는 Set<LottoNumber>가 될 것이다. 일급 컬렉션은 이러한 컬렉션을 Wrapping 하여 Set<LottoNumber>만을 갖는 Lotto라는 일급 컬렉션을 만드는 것을 의미한다.

class Lotto private constructor(private val numbers: Set<LottoNumber>) {
    ....
}

이러한 일급 컬렉션은 그럼 어떠한 장점이 있을까요?

  • 비지니스에 종속적인 자료구조

    로또 번호는 중복되서는 안되며, 서로 다른 6개의 숫자여야 한다. 이러한 규칙을 만족하는 로또만 생성이 가능하다.

  • Collection의 불변성을 보장

    값을 변경할 수 있는 메소드를 만들지 않으면 불변 컬렉션이 된다. 뿐만 아니라 Lotto class의 numbers는 Set으로 선언되있기 때문에 추가, 삭제, 변경이 불가능하다.

  • 상태와 행위를 한 곳에서 관리

    Lotto와 관련된 상태, 행위를 Lotto class에서 관리하기 때문에 이외의 로직에서 추가적으로 구현하거나 계속해서 중복적으로 구현할 필요가 없어진다.

  • 이름이 있는 컬렉션

    기존 Set<LottoNumber>라는 타입의 변수가 있다면 이게 로또인지, 밥인지, 된장인지, 알 수 있는가? 일급 컬렉션을 사용하게 되면 Lotto라는 일급 컬렉션을 사용함으로써 코드의 가독성이 향상 될 수 있다.

참고자료