어떤 개념인가요 ?

일급 컬렉션

Collection을 Wrapping하면서, 그 외 다른 멤버 변수가 없는 상태를 말한다.

객체지향 생활체조 원칙 중 8번째 규칙 “일급 컬렉션을 사용”에서 유래하였다.

쉽게 생각하면, List, Map, Set 처럼 컬렉션을 변수로 사용하지 않고, 컬렉션 하나만을 필드로 갖는 클래스로감싸서 사용하라는 개념이다.

어떤 문제를 해결하려고 나왔나?

일급 컬렉션을 사용하지 않는다면, 원시 컬렉션을 그대로 사용했을 것이다.

이때 어떤 문제점이 있는지 생각해볼 수 있다.

문제점은 아래와 같다.

1. 비즈니스에 종속적인 자료구조 분산

같은 컬렉션을 다루는 로직이 여러한 곳에서 흩어지게 된다.

List<Card> 를 사용한다고 생각하면 카드들의 전체 합계 계산 로직 이 있다면 여러한 곳에서 중복이 되거나 다르게 구현되는 문제가 생긴다.

  • 중복된 코드 존재
  • 중복된 코드로인한 코드 수정시 변경 전파에 대한 오류

2. 컬렉션의 불변성 보장 어려움

기존 원시 컬렉션을 사용한다면 final List<Card> 로 선언하여도 리스트에 대한 기능 .add() 메서드의 기능이 가능하다.

그러므로 재할당의 경우만 막을 수 있고, 내부 상태 변경은 막지 못한다.

즉, 불변의 컬렉션을 만드려면 매번 Collections.unmodifiableList() 같은 코드를 반복해야 한다.

3. 상태와 행위를 한 곳에서 관리할 수 없다.

객체지향에서의 핵심은 데이터와 그 데이터를 다루는 행위를 함께 다루어야 한다. 원시 컬렉션은 데이터일 뿐이고, 그와 관련된 행동을이 다른 외부로 새어 나가게 되는 현상이 생긴다.

4. 컬렉션에 이름을 부여할 수 없다.

List<Card> 이라면 카드가 모여있는 리스트가 어떤 카드들의 리스트인지 명시가 필요하다.

예를들어서 “플레이어의 덱”, “전체 카드들의 리스트” 타입 자체로 의미를 전달 할 수 없다.

어떻게 동작하나? (큰 그림)

  • 생성시에 생성자에서 검증과 방어적 복사 수행하여 객체 생성한다.
  • 그 컬렉션과 관련된 모든 비즈니스 로직을 이 클래스로 응집도 있게 메서드 구성
  • 외부로 데이터를 주어야 할 때에는 수정 불가능한 형태로 보내 캡슐화를 유지한다.

언제 쓰고, 언제 안 쓰나?

쓸 때:

  • 컬렉션을 다루는 비즈니스 로직이 2개 이상 존재할 때,
  • 컬렉션 자체에 도메인적 의미가 있을 때 (List → Deck)
  • 컬렉션에 대한 검증 규칙이 존재할 때 (카드 덱들은 중복되어서는 안된다, 로또 번호는 6개여야 한다.)
  • 불변성이 보장되어야 할 때
  • 컬렉션을 여러 메서드/클래스에서 파라미터로 전달하는데, 그때 같은 검증/가공이 반복될 때
  • 같은 타입의 컬렉션이지만 서로 다른 의미로 사용되어 구분이 필요할 때

안 쓸 때:

  • 단순히 데이터를 잠깐 담아두는 임시 컬렉션
  • 비즈니스 로직이 없는 순수한 DTO 용도로 쓰이는 컬렉션
  • 컬렉션에 부여할 도메인적 의미나 규칙이 없을 때
  • 성능이 중요할 때

결국, 이 컬렉션과 관련된 규칙이나 행위를 떠올릴 수 있는가?

라는 질문에 맞다는 판단이 든다면 일급 컬렉션을 도입해보는게 좋을거 같다고 생각합니다

남에게 설명한다면 어떻게 설명할 것인가?

일급컬렉션은 컬렉션을 사용하는 상황에서 변수로 사용하지 않고, 클래스로 한번 감싼 것입니다.

일급 컬렉션을 사용하는 이유를 생각해보면 아래와 같이 3가지 정도 생각할 수 있습니다.

1. 사용하는 컬렉션에서의 중복된 비즈니스 로직 처리

로또에서 List<LottoNumber> 나 블랙잭에서 List<Card> 처럼

로또들의 숫자들을 다룰 때

  • 로또 숫자들을 다루는 로직들이 여기저기 흩어져있을 수 있다.

카드들의 리스트를 다룰 때,

  • 카드리스트 다루는 로직들이 여기저리 흩어져있을 수 있다.

2. 컬렉션의 불변성 보장

Lottos라는 클래스로 List<LottoNumber> 를 래핑하면 그 객체에 대한 불변성을 보장할 수 있다.

결국 외부에세 Lottos에 메시지를 보내면 그 메시지 요청을 받은 객체 내부에서는 내부에 컬렉션을 처리할 자율적인 존재로서 제한을 둘 수가 있다.

3. 상태와 행위를 한곳에서 관리하며 적절한 비즈니스적인 역할 부여

기존에 원시 컬렉션만 사용한다면 List<Card> 는 상태만 소유하게 된다.

객체지향에서 역할, 책임, 협력을 지향하기 위해서라면 상태와 행위를 같은 곳에서 관리하는게 객체지향적이라고 생각이 드는데요,

원시 컬렉션을 사용하는 것보다 일급 컬렉션을 사용한다면 상태와 행위를 한 클래스에서 관리하고 그 컬렉션에 해당하는 비즈니스적인 로직을 생성시에 검증 후 생성하고, 내부 로직도 응집도있게 구성하고, 데이터를 조회할 때에도 불변성을 지키며 방어적 복사도 가능하게 된다.

결국, 일급 컬렉션을 사용함으로써, 해당 비즈니스 로직에 대한 응집도있는 설계를 가능하게 할 수 있다고 볼 수 있을거 같다.