
이건 일급 컬렉션을 적용하시면
좋을 것 같아요.
우테코 프리코스를 하면서, 뜨거운 감자🥔였던 일급컬렉션 도대체 이게 뭐길래... 객체지향 생활체조 원칙에서 수도없이 강조하고 있는 이 내용을 다뤄보고자 한다.
일급 컬렉션(First Class Collection)
소트웍스 앤솔로지의 객체지향 생활체조 파트의 규칙 8에서 언급된 내용으로 다음과 같은 규칙을 적용하는 것을 목표로 하고 있다.
- 콜렉션을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다.
- 각 콜렉션은 그 자체로 포장돼 있으므로 이제 콜렉션과 관련된 동작은 근거지가 마련된셈이다.
- 필터가 이 새 클래스의 일부가 됨을 알 수 있다.
- 필터는 또한 스스로 함수 객체가 될 수 있다.
- 또한 새 클래스는 두 그룹을 같이 묶는다든가 그룹의 각 원소에 규칙을 적용하는 등의 동작을 처리할 수 있다.
- 이는 인스턴스 변수에 대한 규칙의 확실한 확장이지만 그 자체를 위해서도 중요하다.
- 콜렉션은 실로 매우 유용한 원시 타입이다.
- 많은 동작이 있지만 후임 프로그래머나 유지보수 담당자에 의미적 의도나 단초는 거의 없다.
즉, Collection을 Wrapping 하면서 그 외의 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고 부른다.
아래 예시를 보면 어떻게 생겼는지 바로 이해할 수 있다.
public class Cars {
// 멤버 변수가 하나밖에 없음
private List<Car> carList;
public Cars(List<Car> carList) {
this.carList = carList;
}
}
왜 사용할까?
1) 비지니스에 종속적인 자료구조
2) Collection의 불변성을 보장
3) 상태와 행위를 한 곳에서 관리
4) 이름이 있는 컬렉션
비즈니스에 종속적인 자료구조
서비스 메서드를 구현할 때 해당 메서드 내에서 특정 validation 조건들을 함께 구현한다면 모든 장소에서 validation 검증코드가 들어야하는 문제점이 발생한다. 이러한 문제는 일급 컬렉션을 만들어 특정 조건으로 만들 수 있는 새로운 자료구조를 생성하면 문제를 해결할 수 있다.
public class Cars {
private List<Car> carList;
public Cars(List<Car> carList) {
validateCarName(carList);
validateDuplicateName(carList);
this.carList = carList;
}
...
}
이런식으로 객체 인스턴스를 생성하기 전, validation 을 확인해주고 통과 시에만 인스턴스를 생성하도록 좀 더 명확하게 객체를 만들 수 있다. 이렇게 하면 자료구조를 비즈니스에 종속적으로 구현할 수 있다.
Collection의 불변성을 보장
많은 사람들이 헷갈리는 것 중 하나가 Java에서 final 선언이 불변을 만들어준다고 오해하는 것이다.
final List<Car> carList;
위와 같은 코드에서 CarList는 add, remove 처럼 데이터를 변경 할 수 없을까? ❌No. 변경이 가능하다.
정확히 말하자면 final은 재할당은 불가능하도록 하는 것이지 불변객체로 만들어주는 것은 아니다. 따라서 add, remove 와 같은 메서드로 내용을 변경할 수 있는 것이다.
그렇다면, Java에서 불변객체를 만들어야 한다면 어떤식으로 사용해야할까? 바로, 일급 컬렉션 + wrapper class를 함께 사용하여 해결할 수 있다. 이때 유의할 점 두가지는 다음과 같다. (*우테코 2기 티거님의 일급컬렉션 내용을 참고하여 이해했습니다.)
- getter, setter를 만들지 않아 내부 값의 변경이 불가능하도록 해야한다.
- 생성자를 만들 때 unmodifiableList를 사용하여 불변을 보장해야한다.
return Collections.unmodifiableList(carList);
즉, 일급 컬렉션을 만들 때는 collection 값을 그대로 반환하는 기능을 두지 않는 것이 중요하다. 반환 시, 가공된 값을 반환하거나 unmodifiableList 를 사용하여 불변성을 꼭 보장해주도록 하자.ᐟ.ᐟ
상태와 행위를 한 곳에서 관리
일급 컬렉션은 값과 로직이 함께 존재하여 외부에서 중복된 메서드의 생성을 하는 것을 방지해준다. 예를들어 다음과 같은 요구사항이 있다고 가정해보자.
문제) 대학교 학생들을 학년 별 정보를 List로 관리하고자 할 때 각 학년의 평균 학점을 구하시오.
List<Student> 1학년 = new ArrayList<>();
List<Student> 2학년 = new ArrayList<>();
List<Student> 3학년 = new ArrayList<>();
List<Student> 4학년 = new ArrayList<>();
만약, 학년 별로 List를 관리한다면 특정 학년의 평균학점을 구하는 동일한 로직을 각각의 학년 별 여러 메서드를 구현해야하기 때문에 비효율적이라는 것을 알 수 있다. 이러한 문제는 일급 컬렉션을 생성하고 해당 메서드들을 일급 컬렉션 내에 만들어두어 외부에서는 호출하도록 한다면 상태와 행위를 관리하며 동일한 코드를 중복하지 않아도 된다.
public class Student{
private List<Student> studentList;
public Student(List<Student> studentList){
this.studentList = studentList;
}
public float getGradeAvg(){
//학년 별 학점 평균을 구하는 로직 구현
...
}
}
이름이 있는 컬렉션
학년 별 List 정보를 관리한다면 다음과 같이 했을 때 별 다른 문제를 못찾을 수 있다. 그러나, 프로젝트가 커지고 다른 동료들과 협업하게 된다면 collection에 명확한 네이밍을 필요로 한다.
List<Student> 1학년 = new ArrayList<>();
List<Student> 2학년 = new ArrayList<>();
List<Student> 3학년 = new ArrayList<>();
List<Student> 4학년 = new ArrayList<>();
- 해당 객체들을 검색할 때 변수명 만으로 검색을 할 수 있다. 변수명을 모를 시 검색을 하기 어렵다.
- 단순히 변수명에 의미를 깊게 부여하기 어렵다.
따라서 학년 이름을 일급 컬렉션 자체로 만들어두면 변수명을 넘어서 그 의미를 명확하게 부여할 수 있다.
Freshman freshman = new Freshman(~~);
Sophomore sophomore = new Sophomore(~~);
Junior junior = new Junior(~~);
Senior senior = new Senior(~~);
이처럼 일급 컬렉션은 객체지향 원칙을 잘 따르고 있으며, 필요한 부분에 적재적소하게 사용했을 때 더욱 진가를 발휘한다.
끝으로
우테코 프리코스를 진행하며, 좋은 아이디어를 많이 얻은 것도 사실이지만 제대로 알지 못하고 잘못된 정보를 전하는 것도 많았기에 어떤 것을 사용하고 적용하고자 한다면 나의 프로젝트와 소스를 기반으로 이해하는 습관을 가져야겠다는 생각이 들었다 :)
참고자료
https://jojoldu.tistory.com/412
https://tecoble.techcourse.co.kr/post/2020-05-08-First-Class-Collection/
https://brainbackdoor.tistory.com/140
'Program Language > java' 카테고리의 다른 글
| 프로젝트, 패키지, 메소드, 클래스 네이밍 규칙 (1) | 2023.10.21 |
|---|---|
| 객체지향을 위한 생활체조, 클린코드를 지향합니다. (0) | 2023.10.18 |
| 정확히 모르고 쓰면 사수한테 등짝 스매시 맞는 Thread, 당신도 모르는 사이 쓰고 있었다? (0) | 2023.10.17 |
| 지금까지 당연하게 쓰던 Java8, 함수형 프로그래밍으로 다시 이해하기 (1) | 2023.10.11 |
| 인터페이스 네이밍은 어떻게 해야할까? (0) | 2023.10.10 |