☕ Java
일급 컬렉션이란?
안녕하세요, 지난번 자바의 가비지 컬렉션 발표에 이어서 또 다시 기술 세미나를 맡아 시작하게 되었습니다.
특별히 지난번 가비지 컬렉션 발표에 이어서 또 다른 컬렉션에 대해 발표를 해보면 어떨까? 하는 생각에 (농담입니다)
객체지향에서 크루분들이 어려워하는 개념 중 하나인
일급 컬렉션
에 대해 발표를 진행하기로 하였답니다.일급 컬렉션(first-class-collection)이란?
일급 컬렉션
에 대한 개념은 마틴 파울러
의 책 소트웍스 앤솔러지
에서 처음으로 제시되었습니다.규칙 8: 일급 콜렉션 사용
이 규칙의 적용은 간단하다. 콜렉션을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다. 각 콜렉션은 그 자체로 포장이 되어있으므로 이제 콜렉션과 관련된 동작은 근거지가 마련된 셈이다. 필터가 이 새 클래스의 일부가 됨을 알 수 있다. 필터는 또한 스스로 함수 오브젝트가 될 수 있다. 또한 새 클래스는 두 그룹을 같이 묶는다든가 그룹의 각 원소에 규칙을 적용하는 등의 동작을 처리할 수 있다. 이는 인스턴스 변수에 대한 규칙의 확실한 확장이지만 그 자체를 위해서도 중요하다. 콜렉션은 실로 매우 유용한 원시 타입이다. 많은 동작이 있지만 후임 프로그래머나 유지보수 담당자에게 의미적 의도나 단초는 거의 없다.
위 내용을 정리해보자면,
일급 컬렉션
이란.- 컬렉션만을 멤버 변수로 갖는 클래스
- 컬렉션과 관련된 로직을 클래스 내에 캡슐화
를 뜻한다고 할 수 있겠습니다.
예시를 들어보겠습니다.
Product.java
class Product { private String name; private String category; // 생성자, getter, setter.. }
이러한 상품 객체가 있고
List<Product> products = new ArrayList<>(); products.add(new Product("potato", "vegetables")); products.add(new Product("robot", "toy"));
이한 상품의 리스트 컬렉션이 있다고 가정을 해봅시다.
이를 아래와 같이 감싸는 것을
일급 컬렉션
으로 만들었다고 합니다.ProductCollection.java
public class ProductCollection { private final List<Product> products; public ProductCollection(List<Product> products) { this.products = new ArrayList<>(products); } }
이렇게 일급 컬렉션을 알아보았는데, 이 일급 컬렉션을 사용하면 어떠한 이점이 있는걸까요?
왜 일급컬렉션을 사용해야 한다고 할까요?
왜 일급 컬렉션인가?
1. 캡슐화와 관심사의 분리
public class ProductCollection { private final List<Product> products; public ProductCollection(List<Product> products) { this.products = new ArrayList<>(products); } public ProductCollection filterByCategory(String category) { return new ProductCollection( products.stream() .filter(product -> product.getCategory().equals(category)) .collect(Collectors.toList()) ); } }
일급 컬렉션
을 사용하면 컬렉션에 대한 모든 조작과 로직을 해당 컬렉션 클래스 내부에 구현할 수 있습니다.이로써 로직이 분산되는 것을 방지하고, 관련 로직을 한 곳에서 관리할 수 있습니다.
또 클래스가 단 하나의 책임만을 가지게 됩니다. 이 클래스에서는
products
컬렉션을 관리하는 책임만을 가짐으로써 코드의 가독성과 유지보수성이 향상됩니다.2. 일급 컬렉션의 불변성 보장
public class TeamMembers { private final List<Member> members; public TeamMembers(List<Member> members) { this.members = new ArrayList<>(members); } public void addMember(Member member) { members.add(member); } public void removeMember(Member member) { members.remove(member); } public List<member> getMembers() { return Collections.unmodifiableList(Members); } }
일급 컬렉션
을 사용하면 불변성 보장을 더욱 용이하게 할 수 있어 외부에서의 컬렉션 상태 변경을 방지할 수 있습니다. 이를 통해 데이터의 안정성과 예측 가능성이 증가합니다. 또한 불변성이 보장되면 사이드 이펙트의 가능성이 줄어들며, 멀티 스레드 환경에서의 안정성이 향상됩니다.이 코드에서
TeamMembers
클래스는 어떠한 팀의 멤버들의 리스트를 관리합니다. 여기서 멤버를 추가하거나 제거하는 메서드가 클래스 내부에 구현되어 있고, 일반적인 List.add()
와 같은 메서드를 통해 상태변경이 불가능합니다.즉 이 클래스의 메서드를 통해서만 상태변경이 이루어질 수 있습니다.
getMemebers()
메서드는 불변 리스트를 반환하여 외부에서 컬렉션을 변경하는 것을 방지합니다.이처럼
일급 컬렉션
을 사용하면 컬렉션의 불변성을 내부 로직을 통해 더욱 강력하게 관리할 수 있습니다.3. 비즈니스 규칙과 검증 로직의 중앙화
public class StudentCollection { private final List<Student> students; public StudentCollection(List<Student> students) { this.students = new ArrayList<>(); for (Student student : students) { addStudent(student); } } public void addStudent(Student student) { validateStudent(student); students.add(student); } private void validateStudent(Student student) { if (student.getAge() < 18) { throw new IllegalArguemntException("학생 중 18세 이상만 팀에 들어올 수 있습니다"); } } }
일급 컬렉션
을 통해서 컬렉션에 적용되어야 할 비즈니스 규칙을 클래스 내부에 정의함으로써 일관된 규칙 적용이 가능합니다.컬렉션에 추가되거나 변경되는 요소들에 대한 검증 로직을
일급 컬렉션
내부에 구현함으로써, 데이터의 정합성을 보다 체계적으로 관리할 수 있습니다.addStudent
메서드에서 validateStudent
를 호출하여 학생의 나이가 18세 이상인지 검증합니다. 이렇게 하면 모든 학생 객체가 일관된 규칙을 따를 수 있겠죠?!일급 컬렉션을 사용할 때의 주의 사항
오버엔지니어링의 위험
일급 컬렉션
을 사용할 때 주의해야 할 중요한 점 중 하나는 오버엔지니어링의 위험 입니다. 모든 컬렉션을 일급 컬렉션으로 만드는것이 항상 좋은 것은 아닙니다.예시를 들어보겠습니다.
public class SimpleDataCollection { private final List<Data> dataList; public SimpleDataCollection(List<Data> dataList) { this.dataList = dataList; } }
이 예시에서
SimpleDataCollection
은 복잡한 로직 없이 데이터만을 저장합니다. 이런 경우 일급 컬렉션
을 사용하는 것은 오버엔지니어링이 될 수 있습니다.이처럼
일급 컬렉션
은 컬렉션에 복잡한 비즈니스 로직이나 규칙이 적용될 때 가장 유용합니다. 단순히 데이터를 저장하는 용도라면 굳이 일급 컬렉션
을 사용할 필요는 없겠죠.따라서
일급 컬렉션
을 도입하기 전에 해당 컬렉션에 어떤 로직이나 규칙이 필요한지 고려해야 합니다. 컬렉션의 복잡성과 유지보수의 편의성을 균형있게 고려하는 것이 중요합니다.결론
일급 컬렉션
을 오늘은 함께 알아보았는데요, 우리는 일급 컬렉션이 단순한 데이터의 모음 이상의 가치를 지닌다는 것을 배웠습니다.이는 객체지향 프로그래밍의 근본적인 원칙인 캡슐화와 응집도를 강화하는데 큰 도움을 줍니다.
비즈니스 로직을 한 곳에 집중함으로써, 우리의 코드는 더욱 명확하고, 유지보수하기 쉬우며, 오류 발생가능성을 줄일 수 있습니다.
하지만 모든 상황에서
일급 컬렉션
이 해답은 아닙니다. 우리는 오버엔지니어링의 위험성을 항상 염두하고 일급 컬렉션이 꼭 필요한 상황인지를 항상 고려해야 합니다.오늘 공유한 내용이 실제 프로젝트에서 여러분이 마주칠 문제를 해결하는데 도움이 되길 바랍니다.