티스토리 뷰

JAVA

[Collection] 일급 컬렉션

DevBee 2023. 2. 9. 07:04

개발 중 하나의 리스트를 다양하게 필터해서 다음 처리를 진행하는 로직을 구현했던 적이 있습니다. 이때 같은 리스트를 대상으로 여러가지 연산, 필터 등이 다양한 곳에서 사용되는 것을 보고 선배 개발자분께서 "일급 컬렉션"을 사용하면 훨씬 좋은 코드가 될 것 같다는 조언을 해주셨습니다.

 

그래서 해당 코드를 일급 컬렉션을 사용하여 개선한 경험에 대해 적어보고자 합니다.

 

일급 컬렉션 (First Class Collection)

클래스가 컬렉션을 Wrapping 하면서, 그 외 다른 멤버 변수가 없는 상태를 말합니다.

예를 들면 다음과 같은 리스트가 있다고 할 때

List<Applicant> applicants = new ArrayList<>();

for (String name : names) {
    applicants.add(new Applicant(name));
}

다음과 같이 컬렉션을 Wrapping 하는 것이 일급 컬렉션입니다.

public class Applicants {
    // 멤버 변수가 하나밖에 없다는 것이 중요!!
    List<Applicant> applicants;
    
    public Applicants(List<Applicant> applicants) {
    	this.applicants = applicants;
    }
}

장점

 

  • 비즈니스에 종속적인 자료구조
  • Collection의 불변성을 보장
  • 상태와 행위를 한 곳에서 관리
  • 이름이 있는 컬렉션

장점들을 하나씩 살펴보면서 일급 컬렉션을 좀더 살펴보겠습니다.

 

비즈니스에 종속적인 자료구조

개발 중 사용한 지원자 객체를 예시로 들면 지원자의 나이는 20살 이상이어야 한다. 주소지가 서울이어야 한다. 와 같은 조건이 있었다고 한다면 서비스 메서드에서 이를 구현할 때 지원자 객체 리스트가 들어간 모든 서비스에서 해당 로직이 추가되어야 한다는 단점이 있습니다.

 

이러한 문제는 아래와 같이 일급 컬렉션으로 구현하면 해당 일급 컬렉션을 사용하는 모든 서비스에서는 이미 검증된 리스트를 사용할 수 있게 됩니다.

public class Applicants {
    // 멤버 변수가 하나밖에 없다는 것이 중요!!
    List<Applicant> applicants;
    
    public Applicants(List<Applicant> applicants) {
    	validationAge(applicants);
        validationAddress(applications);
    	this.applicants = applicants;
    }
    
    ...
}

 

Collection의 불변성을 보장

Java에서 final을 사용하면 컬렉션에 재할당은 불가하지만 add, remove 등을 통해 내부 값들을 변경할 수 있습니다. 따라서 아래와 같이 일급 컬렉션 내부 컬렉션에 대한 getter, setter 를 제공하지 않음으로써 불변성을 보장할 수 있습니다.

public class Applicants {
    // 멤버 변수가 하나밖에 없다는 것이 중요!!
    private List<Applicant> applicants;
    
    public Applicants(List<Applicant> applicants) {
    	this.applicants = applicants;
    }
    
    public int getApplicantsSize() {
        return applicants.size();
    }
}

 

참고로 getter, setter를 제공하지 않거나 생성자를 만들 때 unmodifiableList를 사용하여

```return Collections.unmodifiableList(applicants);``` 와 같이 작성할 수 도 있습니다.

 

즉, 일급 컬렉션을 만들 때는 컬렉션 값을 그대로 반환하는 기능을 두지 않고 가공된 값을 반환하거나 unmodifiableList를 사용하여 불변성을 보장해야 합니다.

 

상태와 행위를 한 곳에서 관리

일급 컬렉션은 값과 로직이 함께 존재하여 외부에서의 중복된 메서드의 생성과 같은 문제를 해결해줍니다.

 

예를 들어 모든 지원자를 하나의 리스트로 관리하고 각 등급 별 지원자 수를 구하려고 한다면 각 등급별로 여러개의 중복된 메소드를 생성할 수 있고 코드가 중복되면서 길이가 길어지고 가독성이 떨어질 수 있습니다.

 

따라서 일급 컬렉션을 생성하고 안에 관련된 메서드들을 추가하여 외부에서 호출하여 사용하도록 하면 상태와 행위를 한 곳에서 관리할 수 있게 됩니다.

 

이름이 있는 컬렉션

각 등급별 지원자를 List로 관리한다고 하면 다음과 같이 할 수 있습니다.

List<Applicant> bronze = new ArrayList<>();
List<Applicant> silver = new ArrayList<>();
List<Applicant> gold = new ArrayList<>();
List<Applicant> diamond = new ArrayList<>();

 

위와 같은 경우 1. 변수명으로 각 목록을 관리하고 있어 검색 시 변수명을 모르면 찾을 수 없고 2. 단순히 변수명이라 큰 의미를 부여할 수 없습니다.

 

이 경우 각 등급별로 일급 컬렉션을 사용하여 관리하면 컬렉션 기반 검색이 가능해집니다.

Bronze bronze = new Bronze(~~~);
Silver silver = new Silver(~~~);
Gold gold = new Gold(~~~);
Diamond diamond = new Diamond(~~~);

----------------------------------

public class Bronze {
    private List<Applicant> applicants;
    
    public Bronze(List<Applicant> applicants) {
        this.applicants = applicants
    }
    
    ...
}

...

 

실제 업무에서는 다음과 같이 다양한 필터 조건에 맞게 지원자 리스트를 반환하기 위해 일급 객체를 사용하였습니다.

public class Applicants {
    // 멤버 변수가 하나밖에 없다는 것이 중요!!
    private List<Applicant> applicants;
    
    public Applicants(List<Applicant> applicants) {
    	this.applicants = applicants;
    }
    
    public List<Applicant> getFemaleApplicants() {
        return applicants.stream()
                         .filter(applicant -> applicant.getGender == GenderType.F)
                         ..collect(Collectors.toList());
    }
    
    public List<Applicant> getMaleApplicants() {
        return applicants.stream()
                         .filter(applicant -> applicant.getGender == GenderType.M)
                         ..collect(Collectors.toList());
    }
    
    ...
}

 

참고

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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
글 보관함