이번 첫 코드리뷰를 받으면서 내가 놓치는 부분이 많고, 부족한 부분이 많다는 것을 다시 한 번 느낄 수 있었고, 많은 점을 배울 수 있었다.🥲
피드백 내용을 모두 글로 옮기는 것보다는 링크를 걸어 매번 찾아볼 수 있도록 하는 것이 좋다고 생각되어 링크를 건다!!
(피드백에 대한 나의 생각 혹은 피드백을 수용한 근거를 reply로 제시했기 때문에 나중에 찾아보게 되어도 충분히 상기될 수 있다고 생각된다!!)
그 중에서도 1차 피드백을 받으며 처음 접한 일급 컬렉션
개념에 대해서는 꼭 한 번 정리하고 넘어가는 것이 좋다고 생각되었고, 이렇게 글로 정리하게 되었다.
일급 컬렉션이란 컬렉션을 Wrapping 하면서, Wrapping한 컬렉션 외 다른 멤버 변수가 없는 상태를 말한다.
이렇게 글로만 봐서는 이해가 쉽지 않다. 코드를 통해서 다시 한 번 확인해보자!
public class RacingGame {
private List<Car> cars;
...
}
와 같은 코드에서 List<Car> cars
를 Wrapping하여 다음과 같은 하나의 클래스로 감싸는 것이다.
public class Cars {
// 여기보면 멤버변수가 Wrapping된 컬렉션 외에는 다른 멤버변수가 없다.
private List<Car> cars;
...
}
일급 컬렉션에 대해 공부하며 일급 컬렉션의 소개와 써야할 이유 이 블로그 글을 참고하였다.
여기서는 일급 컬렉션의 이점으로 4가지를 이야기한다.
위 4가지 이점에 대해서 하나하나 나열하는 것은 해당 블로그글을 복사 붙여넣기 하는 그 이상 그 이하도 아니라고 생각되었고, 이번 프리코스에서 일급 컬렉션을 적용하면서 느낀 장점을 위의 언급된 이점과 연관지어서 이야기해보려 한다.
일급 컬렉션을 적용하면서 느낀 가장 큰 장점은 검증
과 같이 어떤 특정 조건을 가진 자료구조를 하나의 클랙스로 Wrapping함으로써 해당 검증이 필요하지 않은 곳으로부터 분리해낼 수 있다는 점이다!
예를 들어서 자동차 경주 구현
미션에서는 자동차들(List<Car>
)의 이름이 중복되면 안된다는 요구사항이 존재한다. 따라서 이를 검증하는 로직이 필요하게 된다. 이를 아래와 같이 작성하였다고 해보자.
public class RacingGame {
private List<Car> cars = new ArrayList<>();
...
private void initRacingCarGame() throws IllegalArgumentException {
String carNames = getCarNames();
cars = initCar(carNames);
}
private static List<Car> initCar(String carNames) throws IllegalArgumentException {
List<Car> carList = new ArrayList<>();
String[] names = carNames.split(SEPARATOR);
validateDuplication(names);
for (String name : names) {
carList.add(new Car(name));
}
return carList;
}
}
하지만 모든 자동차 리스트(List<Car>
)가 위와 같은 요구사항을 만족해야하지 않을 수도 있다. 그렇다면 어떻게 해서 이를 해결할 수 있을까??
우리가 해당 조건(중복 X)을 가지는 자료구조를 하나 만들어냄으로써 이를 해결할 수 있다! 즉, 자동차(Car)들의 리스트인데 중복이 없는 자동차 리스트 자료구조를 만드는 것이다!! 다음의 코드를 보자.
public class RacingGame {
private Cars cars;
private void initRacingCarGame() throws IllegalArgumentException {
String carNames = getCarNames();
cars = new Cars(carNames);
}
}
위와 같이 RacingGame을 만들고, 새로운 일급 컬렉션인 Cars를 만드는 것이다!
public class Cars {
private static final String SEPARATOR = ",";
private static final int FIRST_INDEX = 0;
private List<Car> cars;
public Cars(String carNames) {
initCar(carNames);
}
public int getMaxPosition() {
cars.sort((o1, o2) -> o2.getPosition() - o1.getPosition());
return cars.get(FIRST_INDEX).getPosition();
}
public List<String> getWinner(int position) {
return cars.stream()
.filter(car -> car.isSamePosition(position))
.map(car -> car.getName())
.collect(Collectors.toList());
}
public void progressWithAllCar() {
for (Car car : cars) {
car.progress(getRandomNumber());
}
}
public int getSize() {
return cars.size();
}
public Car getCar(int index) {
return cars.get(index);
}
private void initCar(String carNames) throws IllegalArgumentException {
cars = new ArrayList<>();
String[] names = carNames.split(SEPARATOR);
validateDuplication(names);
for (String name : names) {
cars.add(new Car(name));
}
}
private void validateDuplication(String[] carNames) {
HashSet<String> hashSet = new HashSet<>(Arrays.asList(carNames));
if (hashSet.size() < carNames.length) {
throw new IllegalArgumentException("[ERROR] 자동차 이름이 중복되면 안됩니다.");
}
}
}
이러면 검증에 대한 로직과 해당 검증이 필요한 데이터를 하나로 묶을 수 있게된다! 이는 곧 상태와 행위를 한 곳에서 관리
와도 일맥 상통하는 이야기라고 보여진다. 그리고 해당 자동차 리스트가 필요한 모든 로직에서는 이 일급 컬렉션만 가져다가 쓰면 된다!
컬렉션의 불변을 보장한다는 것이 또 하나의 장점이라고 생각합니다.
위의 본인이 작성한 일급 컬렉션인 Cars
를 보면 해당 클래스를 통한 객체 생성시 initCar() 를 호출함으로써 상태(field)에 값을 할당하고 난 후에는 컬렉션의 값을 변경할 수 있는 메소드가 없도록 하여 불변 컬렉션이 되도록 하였습니다.
위의 Cars
클래스도 보면 List<Car>
에 접근이 불가하기 때문에 해당 리스트의 값을 변경하거나 추가할 수 없습니다.
여기서 final 선언하면 되지 않느냐고 질문할 수 있는데, 이는 앞서 언급한 일급 컬렉션의 소개와 써야할 이유 해당 블로그에서 찾아볼 수 있듯이 단순히 List 필드에 final을 선언하는 것과는 차이가 큽니다!
아래는 final과의 차이를 이해하기 위한 예시 코드입니다!!
//아래의 코드는 정상적으로 동작합니다.
public void testFinal() {
private final List<Car> cars = new ArrayList<>();
cars.add(new Car("a"));
cars.add(new Car("b"));
}
//아래의 코드는 제대로 동작하지 않습니다.
public void testFinal() {
private final List<Car> cars = new ArrayList<>();
cars = new ArrayList<>();
}
이렇게 불변 컬렉션을 만들어 줌으로써 다음과 같은 이점을 얻을 수 있습니다.
각각의 객체들이 절대 값이 바뀔일이 없다는게 보장되면 그만큼 코드를 이해하고 수정하는데 사이드 이펙트가 최소화된다.
출처: 일급 컬렉션의 소개와 써야할 이유
이상으로 일급 컬렉션
에 대한 글을 마무리하도록 하겠습니다. 이번 미션을 통해서는 이름이 있는 컬렉션
에 대한 장점은 느끼지 못하였는데, 향후에 해당 내용에 대한 이점을 스스로 느끼는 순간이 오면 한 번 더 정리해보도록 하겠습니다.😄