Spring

일급 컬렉션이란?

Chemi___6_oj 2023. 2. 1. 15:07

Collection 객체를 감싸면서 ,감싼 컬렉션 외의 다른 멤버 변수가 없는 클래스

 

이게 무슨 의미일까?

 

Collection 객체를 감싼다는 것은 다음과 같다.

public class Menu {

    private final String serialNumber;
    private final String menuName;
    private final int price;

    public Menu(String serialNumber, String menuName, int price) {
        this.serialNumber = serialNumber;
        this.menuName = menuName;
        this.price = price;
    }
    //...
}

Menu 클래스를 Menus라는 클래스에 담는다.

public class Menus{
	private List<Menu> menus;
    
    // ...
}

여기서 Menus 가 일급 컬렉션이며, List<Menu> menus 외 다른 멤버 변수를 가지지 않는다.

 

딱히 대단한 특징이 없어 보이는데, 이런 일급 컬렉션을 왜 사용할까?

 

우선 메뉴가 있고, 커피, 차, 디저트 등이 있다고 하자.

그런데 정해진 serialNumber에 따라 메뉴를 찾는 메소드가 있다고 하자.

그렇다면, 커피, 차, 디저트에 각각 같은 메소드를 모두 구현해줄 건가? 너무 비효율적이다.

 

public class Coffee {
    private final List<Menu> menu;

    public Menu findBySerialNumber(String serialNumber){
        return menu.stream()
                .filter(menu -> menu.getSerialNumber().equals(serialNumber))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("해당하는 항목이 없습니다."));
    }
}    
    
public class Tea {
    private final List<Menu> menu;

    public Menu findBySerialNumber(String serialNumber){
        return menu.stream()
                .filter(menu -> menu.getSerialNumber().equals(serialNumber))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("해당하는 항목이 없습니다."));
    }
}    
    
public class Dessert {
    private final List<Menu> menu;

    public Menu findBySerialNumber(String serialNumber){
        return menu.stream()
                .filter(menu -> menu.getSerialNumber().equals(serialNumber))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("해당하는 항목이 없습니다."));
    }
}

<당시에도 이 코드 중복을 줄이고 싶다는 생각을 했다..>

 

비효율적인 것을 구체적으로 설명해보자면, 각 클래스의 역할이 많아지고, 중복 코드가 많아진다.

이를 해결하기 위한 방법이 일급 컬렉션이다.

 

일급 컬렉션은 상태와 행위를 따로 관리해줄 수 있다.

 

메뉴를 일급 컬렉션으로 만들어보면, 이렇게 만들 수 있다.

public class Menus{
    private List<Menu> menus;
    
    public Menus(List<Menu> menus) {
        this.menus = menus;
    }
    
    public Menu findBySerialNumber(String serialNumber){
    	return menu.stream()
                .filter(menu -> menu.getSerialNumber().equals(serialNumber))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("해당하는 항목이 없습니다."));
    }

그러면 기존 클래스들은

 

public class Coffee {
    private Menus menus;

    public Menu findBySerialNumber(String serialNumber){
        return menus.findBySerialNumber(serialNumber);
    }
    
    
    public class Tea {
        private Menus menus;

        public Menu findBySerialNumber(String serialNumber){
            return menus.findBySerialNumber(serialNumber);
        }


    public class Dessert {
        private Menus menus;

        public Menu findBySerialNumber(String serialNumber){
            return menus.findBySerialNumber(serialNumber);
        }
    
}

이렇게 줄어들 수 있다!

 

찾는 행위를 Menus에 위임하고 우리는 그 로직을 가져와 사용하기만 하면 된다!

이렇게 코드의 중복을 줄이고, 클래스의 역할의 부담을 줄여나갈 수 있다.

 

오해

 

일급 컬렉션은 불변성을 보장한다고 알고 있지만, 다양한 방법으로 불변성을 보장하지 못하는 경우가 나타난다.

일급 컬렉션을 매개변수로 생성하는 클래스라면, 일급 컬렉션에 변화를 주면 그 클래스도 같은 주소값을 공유하기 때문에 변화한다.

getter로 주소값을 재할당하더라도 참조할 수 있어 같은 문제가 발생할 수 있다.

 

그럴 경우 ,  Collection.unmodifiableList 을 getter로 리턴해 클래스를 불변으로 만들 수 있다.