본문 바로가기
☕Java/Java 기본

일급 컬렉션이라는 용어에 대해서

by 캔 2023. 12. 14.

일급 컬렉션이란 하나의 컬렉션 객체를 래핑하고 관련된 동작과 연산들을 정의해 놓은 객체를 말한다. 일급 컬렉션 사용은 ≪소트웍스 앤솔러지: 소프트웨어 기술과 혁신에 관한 에세이》의 객체지향 생활체조에서 언급된 이후 많은 자바 개발자 사이에서 인용되는 원칙 중 하나이다.

 

일급 컬렉션을 사용 및 권장하는 이유는 다음과 같다.

 

도메인에 특화된 자료 구조를 만들고 이름을 붙일 수 있다

자바에서는 컬렉션에 새로운 메서드를 추가할 수 없으므로 컬렉션을 래핑하는 방법을 사용해서 관련된 동작이나 연산을 정의하는 메서드를 추가할 수 있다. 래핑하는 객체에는 이름을 붙일 수 있으므로 해당 컬렉션에 대한 이름을 사용할 수 있게 된다.

 

public class CustomerAccounts {
    private final List<Account> accounts;

    public CustomerAccounts(final List<Account> accounts) {
        this.accounts = accounts;
    }
}

 

위 코드에서는 고객의 소유의 계좌들을 하나로 묶고 그 안에 관련된 연산을 추가하였다. 하나의 계좌는 계좌에 관한 정보들을 갖고 있고 고객은 여러 계좌를 가질 수 있다. 일급 컬렉션을 이용하여 이런 은행 도메인만의 특별한 구조를 표현할 수 있다. 만약 일급 컬렉션을 사용하지 않는다면 매번 고객의 계좌를 List<Account>와 같은 형태로 생성하고 변수명을 지정해 관리해야 한다. 하지만 이제 일급 컬렉션을 사용했으므로 고객의 계좌들을 의미하는 CustomerAccounts라는 이름을 붙여서 좀 더 직관적이고 통일된 명칭을 가지게 되었다.

 

하나의 객체에서 관련된 동작/연산을 관리할 수 있게 해준다

일급 컬렉션을 사용하게 되면 이제 관련된 동작/행위/연산을 추가할 수 있어서 상태와 동작을 한 곳에서 관리할 수 있게 된다.

public class CustomerAccounts {
    private final List<Account> accounts;

    public CustomerAccounts(final List<Account> accounts) {
        this.accounts = accounts;
    }
    
    public BigDecimal totalBalance() {
        return accounts.stream()
                .map(Account::getBalance)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

 

다시 CustomerAccounts의 예를 들어보자. 고객이 보유한 계좌의 총 잔액을 구해야 할 경우 List<Account>를 순회하여 더하여 총합을 얻는다. 일급 컬렉션을 사용하지 않는다면 고객 계좌 총잔액이 필요할 때마다 이 로직을 매번 반복한다. 그리고 반복이 잦아져 이 로직을 메서드로 만들어 놓을 수 있다. 그렇다면 이 메서드는 어디로 가야 할까? 계좌 관련 비즈니스 로직이 있는 계좌 서비스에서 이것을 정의한다고 해보자. 계좌 서비스 하나에서만 사용한다면 모를까 대출이나 다른 서비스에서도 계좌 총잔액을 구하려고 하면 계좌 서비스를 참조해야 한다. 잔액을 구하는 책임은 List<Account>가 가져가는 것이 맞다. 그런데 List<Account>에 이 메서드를 추가할 수 없으니 일급 컬렉션을 사용하는 것이다.

 

컬렉션을 불변으로 사용할 수 있게 해준다

List, Map, Set 등 자바 컬렉션을 그냥 인스턴스화해서 사용할 경우 요소를 추가, 수정, 삭제할 수 있다. 이 컬렉션 인스턴스는 참조형 변수이기 때문에 외부에서 호출하면 해당 객체 요소들을 바로 수정할 수 있게 돼버린다. 그래서 참조형 변수는 불변으로 사용하는 것을 권장한다.

 

하지만 자바 컬렉션의 경우 불변으로 사용하기가 불편하다.

 

private final List<Account> accounts;

 

 

위에서 accounts 변수에 final 예약어를 사용했다. 그렇다면 accounts는 더 이상 수정이 불가능해야 한다. 하지만 클래스 내부에서 accounts.add(), accounts.set(), accounts.remove()를 여전히 쓸 수 있다. 만약 이 accounts 변수가 외부에서 정의되고 사용된다면 언제 어디서든지 변경될 가능성이 있는 것이다. 따라서 일급 컬렉션으로 만들고 변경 가능한 메서드를 생성하지지 않음으로써 컬렉션이 변경되지 않도록 만들어야 한다.

 

사실 아직 위 컬렉션의 변경 가능성이 완전히 없어진 건 아닌데 그것에 대한 내용은 다음에 소개할 내용과 관련이 있어서 여기서는 다루지 않으려고 한다.

 

정리하며

일급 컬렉션도 컬렉션에 메서드를 추가할 수 없는 자바의 한계를 보완하기 위해 자바 개발자들이 찾아낸 하나의 방편이다.(참고로, 자바의 대체재를 자처하는 코틀린에서는 확장 함수나 최상위 함수를 사용해서 컬렉션 연산을 보완할 수 있다) 다른 언어를 사용한다면 모를까, 자바를 사용한다면, 관련된 코드를 모아 중복을 방지하고 컬렉션의 변경을 막을 수 있는 일급 컬렉션 사용을 자주 해보자. 

 

참고

 

더 나은 소프트웨어를 향한 9단계: 객체지향 생활 체조(9)

규칙 8: 일급 콜렉션 사용 이 규칙의 적용은 간단하다. 콜렉션을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다. 각 콜렉션은 그 자체로 포장돼 있으므로 이제 콜렉션과 관련된 동작은

developerfarm.wordpress.com