일급 컬렉션(First Class Collection)
일급 컬렉션은 단일 컬렉션(List, Set, Map 등)을 wrapping하면서, 그외 다른 멤버 변수가 없는 상태를 의미합니다.
즉, 클래스에 필드가 하나만 있고, 해당 필드가 컬렉션을 참조하는 것을 말합니다.
아래의 컬렉션 코드를
List<String> payGroups = new ArrayList<>();
payGroups.add("Shinhan");
payGroups.add("Woori");
payGroups.add("KB");
다음과 같이 Wrpping 하는 것을 말합니다.
public class PayGroups<T> {
private final List<T> pays;
public PayGroups(List<T> pays) {
this.pays = pays;
}
}
이를 통해 얻는 이점은 다음과 같습니다.
- 컬렉션의 불변성 보장
- 비즈니스에 종속적인 구조
- 상태와 행위를 한 곳에 관리 (단일 책임 원칙 준수)
- 의미 있는 이름을 가진 컬렉션
컬렉션의 불변성 보장
일급 컬렉션은 컬렉션의 내용이 변경되지 않도록 보장합니다.
컬렉션은 final을 사용해도, 변수 자체가 불변되는 것이 아닌 재할당만 금지하게 됩니다.
즉, final은 재할당은 불가능 하나
다음과 같이 final을 선언한 컬렉션은 값이 추가되는 것을 확인할 수 있습니다.
Java는 final로 컬렉션의 불변을 만들 수 없기 때문에 일급 컬렉션(First Class Collection)과 래퍼 클래스(Wrapper Class) 등의 방법으로 해결해야 합니다. 다음과 같이 컬렉션을 클래스에 담아 변경할 수 있는 메서드를 막을 수 있습니다.
public class Payments<T> {
// 외부에서 사용할 수 없도록 private 선언
private final List<T> payments;
// 값 할당은 생성자를 통해서만 가능 (생성 시 변경 불가능)
public Payments(List<T> payments) {
this.payments = payments;
}
// 값 가져오기
public List<T> getPayments() {
return payments;
}
}
위 클래스는 생성자와 getter() 외 다른 메서드가 존재하지 않습니다. 또한 List 컬렉션에 접근할 수 없기 때문에 값을 변경/추가할 수 없습니다. 이를 통해 불변 컬렉션을 만들 수 있습니다.
비즈니스에 종속적인 구조
예시로 복권을 관리하는 클래스를 만들어보겠습니다.
복권을 구현하는 조건은 다음과 같습니다.
- 6개의 번호가 존재한다.
- 6개의 번호는 서로 중복되지 않는다.
일반적인 서비스 클래스를 구현하면 다음과 같습니다.
LottoService를 통해 비즈니스 로직을 처리하였습니다.
이러한 방식은 로또 번호가 필요한 모든 장소에서 검증 로직이 들어가야만 합니다.
또한 로또 번호 생성과 번호 검증 등 너무 많은 책임을 가지고 있으며 이로 인해 코드의 관리와 이해가 어려워집니다.
즉, 모든 코드와 도메인을 알고 있지 않다면 문제가 발생할 여지가 있습니다.
우리는 6개의 숫자로만 이뤄졌으며, 서로 중복되지 않아야 하는 자료구조가 필요합니다.
이러한 자료구조는 존재하지 않기 때문에 저희가 직접 만들면 됩니다.
해당 조건으로 직접 자료구조 만들어 위 문제를 해결할 수 있으며, 이러한 클래스을 일급 컬렉션이라 부릅니다.
이를 통해 비지니스에 종속적인 자료구조를 만들어, 이후 발생할 문제를 최소화할 수 있습니다.
상태와 행위를 한 곳에 관리
일급 컬렉션은 컬렉션을 객체로 포장하여 상태와 행위를 한 곳에 관리할 수 있습니다.
즉, 값과 로직이 함께 존재합니다.
예를 들어 여러 카드 결제 방식을 지원하며, 신한카드(Shinhan) 금액의 총합이 필요하다고 가정해보겠습니다.
1. 컬렉션 List를 통해 데이터를 담고
2. Service 또는 Util 클래스에서 필요한 로직(금액 계산) 수행
cardPayments 컬렉션과 계산 로직은 서로 관계가 있으나, 이를 코드로 표현할 수 없습니다.
즉, 객체 지향 설계에 맞춰 객체의 상태(타입)에 따라 행동(메서드)을 통해 계산되도록 강제할 수단이 없습니다.
이러한 이유로 발생할 문제는 다음과 같습니다.
- 히스토리가 관리되지 않을 경우 동일한 기능의 메서드가 중복 생성될 수 있습니다.
- 기존 로직 변경 시, 새로운 개발자는 모든 중복 메서드를 수정해야할지 혼란스러울 수 있습니다.
- 계산 메서드가 누락될 가능성이 있으며, long 타입의 리턴 값 때문에 특정 계산식을 강제할 수 없습니다.
이 또한 일급 컬렉션을 통해 해결할 수 있습니다.
이를 통해 상태와 로직이 한곳에 관리됩니다.
의미 있는 이름을 가진 컬렉션
일급 컬렉션은 의미 있는 이름을 부여할 수 있어 코드를 더욱 명확하고, 이해하기 쉽게 만듭니다.
같은 CardPayment의 컬렉션 모임이여도, 신한카드와 우리카드의 타입은 서로 다릅니다.
이를 구분짓는 방법은 쉽게 변수명을 다르게 하는 것을 생각할 수 있습니다.
List<CardPayment> shinhan = createShinhanPayments();
List<CardPayment> woori = createWooriPayments();
그러나, 이러한 방식은 문제점이 존재합니다.
- 카드 결제 방식이 어떻게 사용되는지 변수명으로만 검색할 수 있습니다.
- 각 개발자마다 변수명을 다르게 지을 수 있어 검색하여 찾기가 어렵습니다.
- 변수명에 불과하기에 의미를 부여하기 어렵습니다.
위 문제 역시 일급 컬렉션을 사용한다면 쉽게 해결할 수 있습니다.
ShinhanCardPayments shinhans = new ShinhanCardPayments(createShinhanCardPayments());
WooriCardPayment swooris = new WooriCardPayments(createWooriCardPayments());
위처럼 명확한 이름이 존재하여 검색이 수월해집니다.
'Java' 카테고리의 다른 글
가변 인자(Vararags) (0) | 2024.06.10 |
---|---|
for loop vs stream forEach (0) | 2024.03.04 |
직렬화(Serialize)란? (2) | 2024.01.22 |