First Class Collection, 일급 컬렉션이란 무엇일까?
Collection을 Wrapping 하면서, 그 외의 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고한다.
이는 두 그룹을 같은 묶거나 그룹의 각 원소에 규칙을 적용하는 동작을 처리할 수 있다.
각설하고 일급 컬렉션을 예제 코드로 만들어보고 왜 일급 컬렉션을 사용해야하는지를 알아보자.
List<Car> cars = new ArrayList<>();
Car 객체 인스턴스를 담는 cars라는 List를 생각해보자.
이를 일급 컬렉션으로 한번 감싸서 사용하면 아래와 같이 만들 수 있다.
public class Cars {
private final List<Car> cars;
public Cars(List<Car> cars) {
this.cars = cars;
}
}
이렇게 사용할 때의 장점은 무엇일까?
1. 비즈니스에 종속적인 자료구조
일급 컬렉션을 사용하지 않을 때 만약 검증 로직과 같은 비지니스 로직이 필요하다면, 서비스 로직에서 그 일을 수행해야하고 이는 단일 책임 원칙을 위배하게된다.
public class CarService {
public void createCar() {
List<Car> cars = createCars(); // 자동차 생성
validateSize(cars); // 지정된 수만큼만 생성되야하는 검증 로직
}
}
이는 자동차들을 만드는 모든 곳에서 검증 로직이 필요하게 된다. 이를 일급 컬렉션을 사용해 재정의한다면
public class Cars {
private static final int CARS_SIZE = 3;
private final List<Car> cars;
public Cars(List<Car> cars) {
validateSize(cars);
this.cars = cars;
}
private void validateSize(List<Car> cars) {
if(cars.size() != CARS_SIZE) {
throw new IllegalArgumentException("자동차는 3개만 존재해야해요!");
}
}
}
위와 같이 private하게 검증 로직을 숨길 수 있을 뿐만아니라
public class CarService {
public void createCar() {
Cars cars = new Cars(createCars());
}
}
서비스는 단지 Cars를 생성하는 책임만 존재하므로 단일 책임 원칙또한 준수하게된다.
2. 불변(Immutable)
이 부분은 나도 깊게 생각하지 못했던 부분이었다.
Java에서의 final을 사용하면 컬렉션을 불변하게 만들 수 있지 않을까?
절대 그렇지 않다. final은 불변을 만들어주는 것이 아닌 재할당을 금지한다.
fianl List<String> list = new ArrayList<>();
list.add("응 불변 아니야~");
list.add("ㅋㅋㅋ");
fianl 키워드를 붙혀도 값을 추가, 삭제하는 것을 막을 수 없다. 즉, 불변이 아니다.
fianl List<String> list = new ArrayList<>();
list = new ArrayList<>(); // ==> 컴파일 에러
이렇게 재할당을 하려할 때 컴파일 에러가 발생한다.
public class Cars {
private fianl List<Car> cars;
public Cars(List<Car> cars) {
this.cars = cars;
}
public List<Car> getCars() {
return cars;
}
}
일급 컬렉션을 사용하게되면 일급 컬렉션에는 생성자와 getCars만 존재하게 하여 List 에 접근자체가 불가하여 값을 변경/추가할 수 없는 불변 컬렉션으로 만들 수 있다.
3. 상태와 행위를 한곳에서!
이는 1번과 유사한데, 일급 컬렉션 내에 행위에 대한 특정 메소드를 넣게 되므로 상태와 행위를 한 곳에 관리하여 코드를 훨씬 더 편리하게 관리할 수 있다. 즉, Cars가 Car의 상태(ex: name, position)뿐 아니라 Car의 행위도 담게 된다는 의미.
4. 이름이 있는 컬렉션
List<Car> 국산차들 = createDomesticCars();
List<Car> 외제차들 = createForeignCars();
이렇게 변수명을 다르게하여 분리할 수 있지만 이는 변수명으로만 검색이 가능하므로 검색이 어렵고 명확한 표현이 불가능하다는 단점이 존재한다.
이를 일급 컬렉션을 사용하면 훨씬 더 명확하게 표현하고 검색이 용이해진다.
DomesticCars domesticCars = new DomesticCars(createDomesticCars());
ForeignCars foreignCars = new ForeignCars(createForeignCars());