일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자바
- 이펙티브 자바
- 클린아키텍처
- 스프링핵심원리
- 코딩테스트
- 김영한
- effectivejava
- ElasticSearch
- 알고리즘정렬
- 티스토리챌린지
- 알고리즘
- 스프링
- k8s
- 스프링 핵심원리
- java
- 자바스크립트
- 스프링부트
- kubernetes
- 예제로 배우는 스프링 입문
- Effective Java
- 카카오 면접
- Spring
- 카카오
- Effective Java 3
- JavaScript
- Sort
- 이차전지관련주
- 오블완
- 엘라스틱서치
- 이펙티브자바
- Today
- Total
Kim-Baek 개발자 이야기
일급 컬렉션이란? 본문
일급 컬렉션(First-Class Collection)은 소프트웨어 설계에서 컬렉션을 별도의 클래스로 감싸서 관리하는 패턴을 말합니다. 이 개념은 컬렉션(List, Set, 등)을 단순히 도메인 객체의 속성으로 사용하는 대신, 컬렉션 자체를 하나의 일급 객체로 취급하여 도메인 로직을 캡슐화하고 책임을 분리하는 데 목적이 있습니다.
일급 컬렉션의 정의
일급 컬렉션은 다음과 같은 특징을 가집니다:
1. 단일 컬렉션 포장: 하나의 도메인 컬렉션만을 포함합니다.
2. 불변성 유지: 컬렉션의 불변성을 보장하여 외부에서 직접 수정할 수 없게 합니다.
3. 비즈니스 로직 포함 가능: 컬렉션 자체에 관련된 도메인 로직을 포함할 수 있습니다.
4. 도메인 용어 사용: 컬렉션을 도메인 용어에 맞춰 네이밍하여 코드의 가독성을 높입니다.
왜 일급 컬렉션을 사용하는가?
일반적으로 도메인 객체가 단순히 컬렉션을 포함할 때 발생할 수 있는 문제점을 해결하기 위해 사용됩니다:
1. 책임 분리: 도메인 객체가 컬렉션과 관련된 로직을 직접 다루지 않고, 컬렉션 전용 클래스로 책임을 분리합니다.
2. 캡슐화 강화: 컬렉션의 내부 구조를 숨기고, 불변성을 유지함으로써 예기치 않은 변경을 방지합니다.
3. 가독성 및 유지보수성 향상: 도메인 용어에 맞는 클래스를 사용하여 코드의 의도를 명확히 하고, 유지보수를 용이하게 합니다.
4. 도메인 규칙 강제: 컬렉션에 추가되거나 제거될 때 특정 규칙을 강제할 수 있습니다.
일급 컬렉션의 구현 예시
예시 시나리오
회원(Member)과 그 회원이 소유한 주문(Order)들을 관리하는 상황을 가정해보겠습니다. 각 회원은 여러 개의 주문을 가질 수 있습니다.
단순한 구현 (일급 컬렉션 미사용)
public class Member {
private Long id;
private String name;
private List<Order> orders = new ArrayList<>();
// 생성자, getter, setter 생략
public void addOrder(Order order) {
this.orders.add(order);
}
public List<Order> getOrders() {
return Collections.unmodifiableList(orders);
}
}
이 경우 Member 클래스가 orders 컬렉션에 대한 모든 책임을 지게 됩니다.
일급 컬렉션 사용
// 주문(Order) 클래스
public class Order {
private Long id;
private String productName;
private int quantity;
// 생성자, getter, setter 생략
}
// 주문 컬렉션 일급 컬렉션 클래스
public class Orders {
private final List<Order> orders;
public Orders(List<Order> orders) {
// 컬렉션 복사 및 불변성 유지
this.orders = Collections.unmodifiableList(new ArrayList<>(orders));
}
public List<Order> getOrders() {
return orders;
}
public void addOrder(Order order) {
// 새로운 리스트를 생성하여 불변성을 유지하는 방식
List<Order> updatedOrders = new ArrayList<>(this.orders);
updatedOrders.add(order);
// 새로운 Orders 인스턴스를 반환하거나, Builder 패턴 등을 사용할 수 있음
}
// 도메인 로직 추가 가능
public int getTotalQuantity() {
return orders.stream().mapToInt(Order::getQuantity).sum();
}
// equals, hashCode 등의 메서드 오버라이드 가능
}
// 회원(Member) 클래스
public class Member {
private Long id;
private String name;
private Orders orders;
public Member(Long id, String name, Orders orders) {
this.id = id;
this.name = name;
this.orders = orders;
}
// 생성자, getter, setter 생략
public void addOrder(Order order) {
this.orders.addOrder(order);
}
public Orders getOrders() {
return orders;
}
}
이와 같이 Orders 클래스가 컬렉션을 감싸면서 관련 로직을 담당하게 됩니다.
일급 컬렉션의 장점
응집도 향상: 컬렉션과 관련된 모든 로직을 한 곳에 모아 응집도를 높입니다.
도메인 규칙 구현 용이: 컬렉션에 대한 특정 비즈니스 규칙을 쉽게 구현하고 강제할 수 있습니다.
불변성 보장: 외부에서 컬렉션을 수정하지 못하게 막아 데이터 무결성을 유지할 수 있습니다.
테스트 용이성: 컬렉션 관련 로직을 별도로 테스트할 수 있어 테스트가 용이해집니다.
가독성 향상: 도메인 용어를 사용하여 코드의 의도를 명확히 합니다.
일급 컬렉션의 단점 및 고려사항
복잡도 증가: 간단한 컬렉션을 감싸기 위해 별도의 클래스를 작성해야 하므로 코드가 다소 복잡해질 수 있습니다.
과도한 응집: 모든 컬렉션을 일급 컬렉션으로 만들려 하면 오히려 코드가 복잡해지고 가독성이 떨어질 수 있습니다.
추가적인 유지보수 비용: 새로운 클래스가 추가되므로 유지보수 비용이 증가할 수 있습니다.
따라서, 모든 컬렉션에 대해 일급 컬렉션을 적용하는 것보다는 도메인 로직이 복잡하거나 컬렉션에 대한 특별한 규칙이 필요한 경우에 한하여 적용하는 것이 좋습니다.
일급 컬렉션 적용 시 고려사항
필요성 판단: 컬렉션에 대한 특별한 비즈니스 규칙이나 로직이 존재하는지 평가합니다.
적절한 네이밍: 컬렉션을 감싸는 클래스의 이름은 도메인 용어에 맞춰 명확하게 지정합니다.
불변성 유지: 컬렉션의 불변성을 유지하여 데이터의 일관성을 보장합니다.
적절한 메서드 제공: 필요한 컬렉션 조작 메서드만을 제공하고, 불필요한 메서드는 최소화합니다.
추가 예시: Java의 Value Object로 활용
일급 컬렉션은 Value Object의 일종으로 볼 수 있습니다. Value Object는 변경 불가능하며, 동일성보다는 동등성(값의 동일성)에 집중하는 객체입니다. 따라서, 일급 컬렉션을 Value Object로 설계할 때는 다음을 고려해야 합니다:
불변성: 필드를 final로 선언하고, 외부에서 변경할 수 없도록 합니다.
동등성 구현: equals와 hashCode 메서드를 오버라이드하여 값 기반의 동등성을 구현합니다.
직렬화 지원: 필요에 따라 Serializable 인터페이스를 구현하여 직렬화를 지원할 수 있습니다.
public class Orders {
private final List<Order> orders;
public Orders(List<Order> orders) {
this.orders = Collections.unmodifiableList(new ArrayList<>(orders));
}
public List<Order> getOrders() {
return orders;
}
// equals and hashCode based on 'orders' list
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Orders orders1 = (Orders) o;
return orders.equals(orders1.orders);
}
@Override
public int hashCode() {
return orders.hashCode();
}
}
'개발 > java basic' 카테고리의 다른 글
리액티브 프로그래밍이란 (0) | 2024.11.17 |
---|---|
제네릭 타입 소거 (컴파일/런타임) (0) | 2024.11.16 |
제네릭이란 무엇일까 (0) | 2024.11.12 |
자바에서 코틀린 - 6장 자바에서 코틀린 컬렉션으로 (0) | 2023.04.17 |
자바에서 코틀린 - 5장 빈에서 값으로 (0) | 2023.04.13 |