개요
우테코 프리코스를 진행하면서 클린 코드 원칙을 준수해야 했습니다.
그중에서 "컬렉션에 대해 일급 컬렉션을 적용했는가?"라는 항목이 존재했고, 처음 들어보는 단어였기에 관심을 갖게 되었습니다.
일급 컬렉션은 소트웍스 앤솔로지의 객체지향 생활 체조 원칙에서 언급된 단어입니다.
일급 컬렉션이 무엇인지 그리고 언제 사용하면 좋을지 알아보겠습니다.
일급 컬렉션
일급 컬렉션은 컬렉션을 Wrapping 하면서, 컬렉션 이외의 다른 멤버 변수를 가지지 않는 클래스를 의미합니다.
컬렉션을 단순히 데이터 보관용 객체로 두지 않고, 하나의 객체로 추상화해 책임을 부여한 클래스입니다.
컬렉션을 클래스로 포장하여 책임을 갖기 때문에 컬렉션에 대한 비즈니스 로직을 내부에 작성하고 관리할 수 있습니다.
예시를 들어보겠습니다.
public class Product {
private String name;
private String category;
private int price;
public Product(String name, String category, int price) {
this.name = name;
this.category = category;
this.price = price;
}
}
//상품 리스트
List<Product> products = new ArrayList<>();
products.add(new Product("apple","fruit",1000));
products.add(new Product("banana","fruit",1500));
상품이라는 객체가 존재하고, 상품의 컬렉션이 존재합니다. 이를 일급 컬렉션으로 만들면 다음과 같습니다.
public class Products {
List<Product> products;
public Products(List<Product> products) {
this.products = products;
}
}
왜 일급 컬렉션을 사용할까?
1. 클래스가 하나의 책임만 가지도록 하는 객체 지향 원칙(SRP, 단일 책임 원칙)을 지킬 수 있다.
상품들의 가격을 종합해야 하는 로직, 카테고리로 상품을 필터 해야 하는 로직을 서비스 단에서 구현해서 사용한다면 어떻게 될까요?
해당 로직이 필요한 서비스에 중복적으로 작성하고 사용해야 합니다.
만약 해당 로직에 수정이 필요하다면, 두 로직을 모두 수정해야 합니다.
이처럼 컬렉션에 대한 로직을 일급 컬렉션 내부에 작성하여 로직의 분산을 막을 수 있습니다.
따라서 Products 클래스는 상품들의 리스트를 관리하는 책임만을 갖도록 설계할 수 있습니다.
public class Products {
List<Product> products;
public Products(List<Product> products) {
this.products = products;
}
//상품의 가격을 더해서 반환한다.
public int totalPrice() {
return products.stream()
.mapToInt(Product::getPrice)
.sum();
}
}
2. 컬렉션 생성 시 유효성 검사
상품 컬렉션에 대한 검증이 필요하다고 해보겠습니다.
예를 들어 상품을 최대 10개까지만 구매 가능하다는 규칙이 존재합니다.
이러한 로직은 어디서 구현해야 할까요?
서비스 로직에 작성했다면 다른 사람이 해당 서비스 로직에서 다른 작업을 하면서 다음과 같은 고민을 할 수 있습니다.
모든 List<Products>에 검증 로직을 사용해야 하는가?
이러한 고민을 해결할 방법이 일급 컬렉션이라고 생각합니다.
public class Products {
List<Product> products;
public Products(List<Product> products) {
this.products = products;
}
private void validate(List<Product> products) {
if (products.size() > 10) {
throw new IllegalArgumentException("상품은 10개 이하로 구매할 수 있습니다.");
}
}
public int totalPrice() {
return products.stream()
.mapToInt(Product::getPrice)
.sum();
}
}
컬렉션에 대한 검증 로직을 클래스 내부에 작성함으로써 데이터 정합성을 객체 스스로 관리할 수 있습니다.
3. 불변성을 보장할 수 있다.
일급 컬렉션을 사용하여 외부에서의 컬렉션 상태 변경을 방지할 수 있습니다.
public class Products {
private final List<Product> products;
...
public void addProduct(Product product) {
products.add(product);
}
public void removeProduct(Product product) {
products.remove(product);
}
public List<Product> getProducts() {
return Collections.unmodifiableList(products);
}
...
}
상품을 추가하고 제거하는 메서드를 클래스 내부에 구현하여 List.add()와 같은 메서드를 통해 상태 변경이 불가능하도록 막고,
클래스 내부의 메서드를 사용하여 상태를 변경하도록 할 수 있습니다.
또한 일급 컬렉션의 값을 가져와야 하는 경우, 불변 리스트(unmodifiableList)를 반환하여 외부에서 컬렉션을 변경하는 것을 방지할 수 있습니다.
그럼, 모든 컬렉션을 일급 컬렉션으로 변경해야 할까?
그렇지 않습니다.
모든 컬렉션을 일급 컬렉션으로 만든다면 관리해야 하는 클래스가 과도하게 많아질 수 있습니다.
따라서 컬렉션 자체에 비즈니스 로직이나 검증이 필요한 경우, 즉 단순한 데이터 보관을 넘어 도메인 규칙을 표현해야 하는 경우에만 일급 컬렉션을 도입하는 것이 좋다고 생각합니다.
참고
일급 컬렉션 (First Class Collection)의 소개와 써야할 이유
일급 컬렉션(First-Class Collection)이란?
'Teck Stack > Java' 카테고리의 다른 글
| [Java] 동시성 제어 2탄 - 세마포어(Semaphore) (0) | 2025.12.28 |
|---|---|
| [Java] 동시성 제어 1탄 - Lock (1) | 2025.12.27 |
| [JAVA] Static은 언제 사용할까? (0) | 2025.10.24 |
| [Java] Error와 Exception (0) | 2025.10.08 |