일급 컬렉션이란?
일급 컬렉션은 컬렉션을 멤버 변수로 갖는 클래스를 만드는 규칙이다.
`마틴 파울러`가 제한한 `컬렉션 캡슐화` 원칙에서 시작되었고 이후 컬렉션을 다루는 방식에 SOLID의 단일 책임 원칙(SRP)을 도입했다.
일급 컬렉션은 아래 3가지 조건을 만족하는 클래스다.
- 컬렉션을 멤버 변수로 갖는 클래스
- 해당 클래스는 컬렉션을 private 필드로 가짐
- 컬렉션에 관련된 로직만 수행하는 메서드를 가짐
public class Student {
private String id;
private String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
}
`Student`클래스를 그냥 컬렉션으로 `School`클래스에서 사용하면 다음과 같다.
public class School {
private List<Student> students = new ArrayList<>();
// 학생 추가
public void addStudent(Student student) {
// 최대 100명까지만 등록 가능
if (students.size() >= 100) {
throw new IllegalStateException("학교 정원이 초과되었습니다.");
}
students.add(student);
}
// 학생 목록 조회
public List<Student> getStudents() {
return students;
}
// 학생 수 조회
public int getStudentCount() {
return students.size();
}
// 학교 관리 메서드들...
}
getStudents()를 외부로 그대로 반환해 외부에서 직접 수정이 가능하고, `School` 클래스는 두 가지 책임을 가지고 있다.
- 학교 관리 책임
- 학생 목록 관리 책임
학생 목록의 규칙이 변경될 때도, `School` 클래스를 수정해야 하고, 학교 관리 기능이 변경될 때도 `School`클래스를 수정해야 한다. ➡️ 이는 단일 책임 원칙(SRP)를 위반한다.
// 일급 컬렉션: 학생 목록 관리
public class Students {
private final List<Student> values;
private static final int MAX_STUDENT_COUNT = 100;
// 빈 학생 목록 생성
public Students() {
this.values = new ArrayList<>();
}
// 기존 학생 목록으로 생성
public Students(List<Student> students) {
validateMaxSize(students);
this.values = new ArrayList<>(students);
}
// 최대 인원 검증
private void validateMaxSize(List<Student> students) {
if (students.size() > MAX_STUDENT_COUNT) {
throw new IllegalArgumentException("학생은 최대 " + MAX_STUDENT_COUNT + "명까지만 등록할 수 있습니다.");
}
}
// 학생 추가
public void add(Student student) {
if (values.size() >= MAX_STUDENT_COUNT) {
throw new IllegalStateException("학생은 최대 " + MAX_STUDENT_COUNT + "명까지만 등록할 수 있습니다.");
}
values.add(student);
}
// 학생 목록 조회 (수정 불가능한 리스트 반환)
public List<Student> getList() {
return Collections.unmodifiableList(values);
}
// 학생 수 조회
public int size() {
return values.size();
}
}
// 일급 컬렉션을 사용하는 학교 클래스
public class School {
private Students students;
public School() {
this.students = new Students();
}
public void addStudent(Student student) {
students.add(student);
}
public List<Student> getStudentList() {
return students.getList();
}
public int getStudentCount() {
return students.size();
}
}
반면, 일급 컬렉션 방식을 사용하면 `Students`클래스는 학생 목록 관리만 담당하게 되고, 이를 `School`클래스는 가져다 쓰기만 하면 된다. 학생 목록 관리 기능이 수정된다면 우리는 `Students`클래스만 수정하면된다.
💡 여기서 중요한 것은 `School`클래스는 `Students`클래스가 어떻게 변경되는지 몰라도 된다는 것이다!
이는 객체지향의 핵심 원칙인 캡슐화와 인터페이스 분리를 지키는 것이다.
일급 컬렉션의 장점
비즈니스 로직 캡슐화
위 예시처럼 `Student`컬렉션과 관련된 비즈니스 로직은 모두 `Students`에서 관리할 수 있다.
이는 로직이 분산되지 않고 중앙화되고 해당 컬렉션과 컬렉션을 다루는 메서드를 하나의 클래스로 묶어 외부로부터 감출 수 있다.
불변성 보장
`School`클래스에서 `Students`의 내부 컬렉션을 직접 수정할 수 없고 제어할 수 없다.
이를 통해 의도하지 않은 상태 변경을 방지할 수 있어, 버그가 감소하고 멀티 스레드 환경에서 안전하다.
SRP 보장
`Students`는 컬렉션 관리라는 단일 책임만 갖는 클래스이다. 학생 관리 기능을 수정할 때는 `Students`클래스에만 접근해 수정하면 되기 때문에 유지보수가 용이 하고, 테스트 범위가 명확해 테스트 하기가 쉽다.
이름이 있는 컬렉션
일급 컬렉션은 비즈니스 로직에 특화된 명확한 이름을 가질 수 있다. 이를 통해 코드의 의도가 명확하고 컴파일 타임에 타입 체크가 가능하다.
'Java' 카테고리의 다른 글
| [Java] 다이아몬드 문제 (feat. 다중 상속) (0) | 2025.05.15 |
|---|---|
| [Java] 동일성과 동등성에 관하여 (0) | 2025.03.17 |
| [Java] equals()와 hashCode()에 관하여 (0) | 2025.03.14 |
| [Java] 얕은 복사와 깊은 복사 (4) | 2025.03.11 |
| [Java] Checked Exception과 Unchecked Exception (1) | 2025.03.07 |