NoTimeForDawdling

일급 컬렉션(First Class Collection) 본문

Java

일급 컬렉션(First Class Collection)

Room_Energy 2021. 3. 1. 16:30

예전에 스터디를 참여하면서 일급 컬렉션에 대해 공부하고, 적용해 본 적이 있었습니다.

일급 컬렉션이 무엇인지, 어떤 장점이 있는지 지금부터 알아보겠습니다.

일급 컬렉션이란?

  • 일급 컬렉션은 Collection을 Wrapping 하면서, 그 외 다른 멤버 변수가 없는 상태를 뜻합니다.
  • 중요한 점은 Collection을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다는 겁니다.

간단한 코드 예제

List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);

위의 코드를 아래와 같이 Wrapping 하는 것을 얘기합니다.

public class Numbers {
    private List<Integer> numbers;
    
    public FirstClass(List<Integer> numbers){
        this.numbers = numbers;
    }
}

일급 컬렉션의 장점

  1. 비즈니스에 종속적인 자료구조입니다.

  2. Collection의 불변성을 보장합니다.

  3. 상태와 행위를 한 곳에서 관리합니다.

  4. 이름이 있는 컬렉션입니다.

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

레이싱 게임을 한다고 가정하겠습니다.

레이싱 게임을 하기 위해서는 경주를 할 자동차들이 필요합니다. 자동차의 최대 개수는 10개로 제한합니다.

public class RacingGame {
    private static final int MAX_SIZE = 10;

    public void raceStart() {
        List<Car> cars = createCars();
        validateSize(cars);
        // race logic start
    }

    private void validateSize(List<Car> cars) {
        if (cars.size() > MAX_SIZE) {
            throw new IllegalArgumentException(String.format("Exceeded maximum number of cars. input cars size: %d", cars.size()));
        }
    }

    private List<Car> createCars() {
        // createCars
    }
}

위 코드를 보면 RacingGame 클래스에서 race에 관련된 로직뿐만 아니라 자동차에 관한 검증 로직까지 처리합니다. 이렇게 된다면 자동차를 사용하는 모든 메서드에서 검증 로직을 추가해야 합니다.

 

이 문제를 해결하기 위해서 해당 조건을 만족하는 객체를 만들어주면 됩니다. 이게 바로 일급 컬렉션입니다.

// 일급 컬렉션
public class Cars {

    private final int MAX_SIZE = 10;
    private final List<Car> cars;

    public Cars(List<Car> cars) {
        validateCount(cars);
        this.cars = cars;
    }

    private void validateCount(List<Car> cars) {
        if (cars.size() > MAX_SIZE) {
            throw new IllegalArgumentException(String.format("Exceeded maximum number of cars. input cars size: %d", cars.size()));
        }
    }
}
public class RacingGame {
    
    public void startRace() {
        final Cars cars = new Cars(createCars());
        // race logic start
    }
  
    private List<Car> createCars() {
        // createCars
    }
}

이처럼 Car 관련 비즈니스 로직(여기서는 최대 개수)을 Cars라는 일급 컬렉션에서 해결할 수 있는, 비즈니스에 종속적이 자료구조가 만들어졌습니다.

2. Collection의 불변성을 보장

일급 컬렉션은 컬렉션의 불변성을 보장해 줍니다. 그렇다면 final과의 차이점은 무엇일까요?

 

  • final은 단순히 재할당만 금지할 뿐, 구조적 변경은 허용합니다.(add, remove 허용)
  • 일급 컬렉션은 단순히 final을

    • 구조적 변경의 허용 유무는 불변 객체를 만드는데 매우 중요합니다.

    • 불변 객체를 만들기 위해서는 해당 객체의 값을 변경할 수 있는 메서드가 없어야 하기 때문입니다.

 

public class Cars {

    private final List<Car> cars;

    public Cars(List<Car> cars) {
        this.cars = cars;
    }

    public int getTotalPrice() {
        return cars.stream()
                .mapToInt(Car::getPrice)
                .sum();
    }
}

위 코드는 일급 컬렉션입니다.이 코드를 보면 구조적 변경을 할 수 없고, 내부 값도 변경할 수 있는 메서드는 존재하지 않습니다.

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

일급 컬렉션은 값과 로직이 함께 존재합니다. 즉, 응집도가 높다는 걸 의미합니다.

예를 들어 여러 Car들이 모여있고, 이 중 우리나라에서 생성한 Car의 개수를 얻어야 한다고 가정해보겠습니다.

public class Cars {

    private final List<Car> cars;

    public Cars(List<Car> cars) {
        this.cars = cars;
    }

    public Long getKrCarCount() {
        return cars.stream()
                .filter(car -> car.isKrCar())
                .count();
    }
}

위와 같이 Cars라는 일급 컬렉션을 만들고, 그 안에 우리나라의 Car 개수를 가져올 수 있는 코드를 구현했습니다.

즉, 일급 컬렉션을 사용함으로써 상태와 로직을 한 곳에서 관리할 수 있게 됐습니다.

4. 이름이 있는 컬렉션

예를 들어 한국 자동차 그룹과 미국 자동차 그룹을 구분해야 한다고 가정해보겠습니다.

다음과 같이 변수명을 다르게 하여 구분했습니다.

List<Car> KrCars = createKrCars();
List<Car> UsaCars = createUsaCars();

위 코드의 문제점은 다음과 같습니다.

 

  • 한국 자동차의 그룹이라는 뜻은 개발자마다 다르게 지을 수 있습니다. 그렇기 때문에 변수명으로 유추한 그룹이 원하는 그룹이 아닐 수도 있습니다.
  • 변수명에 불과하기 때문에 의미를 부여하기 어렵습니다.

  • 중요한 값임에도 이를 표현할 명확한 단어가 없습니다.

위 문제의 해결책으로 각각의 그룹을 일급 컬렉션으로 만들면 됩니다.

 

KrCars krCars = new KrCars(createKrCars());
UsaCars usaCars = new UsaCars(createUsaCars());

위와 같이 한국 자동차와 미국 자동차 각각의 일급 컬렉션을 만들어서 해당 문제점들을 해결할 수 있습니다.

 

참고

https://jojoldu.tistory.com/412