| 일급 컬렉션(First-Class Collection)
일급 컬렉션은 컬렉션을 포함한 클래스를 만들어 사용하는 방식을 말함
이 클래스는 해당 컬렉션 하나만 필드로 가지며, 그 컬렉션과 관련된 동작을 함께 정의함
- 단일 책임 원칙: 일급 컬렉션은 하나의 컬렉션에 대한 책임만 가짐
- 불변성: 컬렉션의 불변성을 보장할 수 있음
- 상태와 행위의 결합: 컬렉션과 관련된 동작을 한 곳에서 관리
- 비즈니스 로직 캡슐화: 컬렉션과 관련된 비즈니스 로직을 해당 클래스 내에 캡슐화
- 이름이 있는 컬렉션: 컬렉션의 의도를 명확히 표현할 수 있음
일급 컬렉션은 마치 특별한 용도의 상자와 같습니다. 일반 컬렉션이 그냥 물건을 담는 평범한 상자라면, 일급 컬렉션은 특정 종류의 물건만 담을 수 있고, 그 물건들을 어떻게 다뤄야 하는지 설명서가 함께 붙어있는 특별한 상자입니다.
public class LottoNumbers {
private final List<Integer> numbers;
public LottoNumbers(List<Integer> numbers) {
validate(numbers);
this.numbers = new ArrayList<>(numbers);
}
private void validate(List<Integer> numbers) {
if (numbers.size() != 6) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
if (new HashSet<>(numbers).size() != 6) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다.");
}
if (numbers.stream().anyMatch(n -> n < 1 || n > 45)) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이여야 합니다.");
}
}
public boolean contains(int number) {
return numbers.contains(number);
}
public int matchCount(LottoNumbers other) {
return (int) this.numbers.stream()
.filter(other.numbers::contains)
.count();
}
@Override
public String toString() {
return numbers.toString();
}
}
LottoNumbers는 로또 번호 리스트를 감싸는 일급 컬렉션임
생성자에서 유효성 검사를 수행하여 항상 올바른 로또 번호만 생성되도록 함
contains 메서드로 특정 번호의 포함 여부를 확인할 수 있음
matchCount 메서드로 다른 로또 번호와의 일치 개수를 쉽게 계산할 수 있음
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
LottoNumbers lotto = new LottoNumbers(numbers);
System.out.println(lotto); // [1, 2, 3, 4, 5, 6]
System.out.println(lotto.contains(3)); // true
LottoNumbers otherLotto = new LottoNumbers(Arrays.asList(4, 5, 6, 7, 8, 9));
System.out.println(lotto.matchCount(otherLotto)); // 3
로또 번호와 관련된 모든 로직을 한 곳에서 관리할 수 있고, 재사용성과 유지보수성이 향상됨
| 기존 단순 클래스 방식: 일반적으로 List<Integer>나 Integer[]를 사용할 경우
List<Integer> lottoNumbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 데이터 검증
if (lottoNumbers.size() != 6) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
// ... 다른 검증 로직들 ...
// 특정 번호 포함 여부
boolean contains = lottoNumbers.contains(3);
// 일치 개수 계산
List<Integer> otherNumbers = Arrays.asList(4, 5, 6, 7, 8, 9);
int matchCount = (int) lottoNumbers.stream()
.filter(otherNumbers::contains)
.count();
로또 번호를 사용할 때마다 검증, 포함 여부 확인, 일치 개수 계산 등의 로직을 반복해서 작성해야 함
-> 내부 상태에 직접 접근
| 일급 컬렉션 방식: LottoNumbers 클래스를 사용할 경우
LottoNumbers lotto = new LottoNumbers(Arrays.asList(1, 2, 3, 4, 5, 6));
// 생성 시점에 이미 데이터 검증 완료
boolean contains = lotto.contains(3);
LottoNumbers otherLotto = new LottoNumbers(Arrays.asList(4, 5, 6, 7, 8, 9));
int matchCount = lotto.matchCount(otherLotto);
생성 시점 검증: 객체가 생성될 때 자동으로 데이터 검증이 이루어집니다. 잘못된 데이터로 객체가 생성되는 것을 방지함
캡슐화: 로또 번호와 관련된 모든 동작(검증, 포함 여부 확인, 일치 개수 계산 등)이 LottoNumbers 클래스 내부에 캡슐화되어 있음
재사용성: 한 번 정의해 놓은 로직을 여러 곳에서 쉽게 재사용할 수 있음
가독성: 코드의 의도가 더 명확해지며, 비즈니스 로직을 이해하기 쉬워짐
유지보수성: 로또 번호와 관련된 요구사항이 변경될 경우, LottoNumbers 클래스만 수정하면 됨
-> 객체 내부에서 판단하고 결과만 반환
∴ 따라서, 일급 컬렉션을 사용하면 로또 번호와 관련된 모든 책임을 한 클래스에 모아 관리할 수 있게 됨
만약 로또 번호가 6개에서 7개로 변하고, 중복될 수 있게 기준이 변경된다면
-> 단순 클래스 : 모든 사용 지점 수정 필요
-> 일급 컬렉션 : LottoNumbers 클래스의 validate 메서드만 수정
| 장점
- 불변성(Immutability) 보장
일급 컬렉션은 내부 컬렉션을 final로 선언하고, 생성자에서 새로운 컬렉션을 만들어 할당함으로써 불변성을 쉽게 보장할 수 있음
- 상태와 행위의 응집
데이터(로또 번호)와 그 데이터를 조작하는 메서드(검증, 비교 등)가 한 클래스에 모여 있어 응집도가 높아짐
- 테스트 용이성
로또 번호와 관련된 로직을 테스트할 때, LottoNumbers 클래스만 집중적으로 테스트하면 됨
- 확장성
나중에 로또 규칙이 변경되거나 새로운 기능이 추가되어도 LottoNumbers 클래스만 수정하면 됨
- 명확한 네이밍
List<Integer>보다 LottoNumbers라는 이름이 더 명확하게 의도를 전달함
'Java' 카테고리의 다른 글
파라미터 타입이 다른 공통 로직 모듈화 - 제네릭 (0) | 2024.12.11 |
---|---|
파라미터 타입이 다른 공통 로직 모듈화 - 인터페이스 (0) | 2024.12.11 |
jsp (0) | 2024.12.10 |
Java Stream - Stream, map, collect()로 리스트 객체 가공 (0) | 2024.10.29 |
정적 팩토리 메서드 (1) | 2024.10.21 |