객체지향설계 스터디를 참여하면서 정말 정말 부족한 점이 많다는 것을 깨닫고 있다.
특히 나는 객체지향적인 코드와 거리가 아주 먼 코드를 작성하고 있었다..
덕분에 스터디가 많은 도움이 되고 있으며 이것들을 놓치지 않기 위해 정리하고자 한다.
그중 지난주 스터디에서 제일 많이 들은 일급 컬렉션을 정리하고자 한다.
일급 컬렉션이란?
Colletion을 Wrapping 하면서, Colletion 외의 다른 멤버 변수가 없는 상태를 일급 컬렉션이라고 한다.
일급 컬렉션이란 단어는 '소트웍스 앤솔로지'의 객체지향 생활체조 파트에서 언급이 되어있습니다.
규칙 8
일급 컬렉션 사용이 규칙의 적용은 간단하다.
컬렉션을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다.
각 콜렉션은 그 자체로 포장돼 있으므로 이제 컬렉션과 관련된 동작은 근거지가 마련된 셈이다.
필터가 이 새 클래스의 일부가 됨을 알 수 있다.
필터는 또한 스스로 함수 객체가 될 수 있다.
또한 새 클래스는 두 그룹을 같이 묶는다든가 그룹의 각 원소에 규칙을 적용하는 등의 동작을 처리할 수 있다.
이는 인스턴스 변수에 대한 규칙의 확실한 확장이지만 그 자체를 위해서도 중요하다.
컬렉션은 실로 매우 유용한 원시 타입이다.
많은 동작이 있지만 후임 프로그래머나 유지보수 담당자에 의미적 의도나 단초는 거의 없다.
- 소트웍스 앤솔로지 객체지향 생활체조 편
일급 컬렉션의 코드는 아래와 같다.
List<Student> studentList = new ArrayList<>();
for (String studentName : studentArr) {
studentList.add(new Student(studentName));
}
이처럼 학생과 관련된 collection이 있을 때,
public class Students {
private List<Student> studentList;
public Students(List<Student> studentList) {
this.studentList = studentList;
}
}
해당 collection을 wrapping 하는 것이 일급 컬렉션이다.
그렇다면 왜 일급 컬렉션을 사용할까?
일급 컬렉션을 사용하는 이유
일급 컬렉션을 사용하는 이유는 확실한 장점이 있기 때문이다.
- 비즈니스에 종속적인 자료구조
- Collection의 불변성을 보장
- 상태와 행위를 한 곳에서 관리
- 이름이 있는 컬렉션
1. 비즈니스에 종속적인 자료구조
비즈니스에 종속적인 자료구조는 쉽게 말해서,
서비스 메서드에서 호출되는 비즈니스 로직의 문제점을 해결하기 위함이다.
작년 우테코 프리코스 3주 차 미션인 로또 게임을 예시로 들어보겠다.
로또 번호의 조건은 1 이상 45 이하면서, 중복되지 않아야 하고, 총 6개의 숫자를 뽑아야 한다.
때문에 로또 번호를 생성할 때마다 위 세 가지 조건을 모두 만족시켜야 한다.
즉, 로또 번호가 필요한 모든 장소에 검증 로직이 들어가야만 한다.
하지만 이러한 경우 모든 코드나 도메인을 알고 있지 않으면 이 검증로직이 언제 필요한지 알 수 없다.
때문에 로또 번호의 조건을 만족시킬 자료구조가 필요해졌고, 이를 직접 만드는 과정에서 필요한 것이 바로 일급 컬렉션이다.
public class Lotto {
private static final int LOTO_NUMBERS_SIZE = 6;
private final List<Long> lottoNumbers;
public Lotto(List<Long> lottoNumbers) {
validateValue(lottoNumbers)
validateSize(lottoNumbers)
validateDuplidate(lottoNumbers)
this.lottoNumbers = lottoNumbers;
}
private void validateValue(List<Long> lottoNumbers) {
}
private void validateSize(List<Long> lottoNumbers) {
}
private void validateDuplidate(List<Long> lottoNumbers) {
}
위 코드가 바로 비즈니스 로직에 종속적인 자료구조이다.
해당 조건을 통과해야만 생성할 수 있는 자료구조이기 때문에 이처럼 작성한다면 위에서 언급한 문제들을 해결할 수 있다.
2. Collection의 불변성을 보장
일급 컬렉션은 컬렉션의 불변을 보장한다.
여기서 final과의 차이점을 짚자면
final은 불변을 보장하는 것이 아니라, 재할당만 금지하는 것이다.
그렇다면 일급 컬렉션은 불변을 어떻게 보장하느냐.
처음부터 값을 변경/추가할 수 없도록 구현한다.
public class Cars {
private final List<Car> cars;
public Cars(List<Car> cars) {
this.cars = cars;
}
public int getCountCars() {
return cars.stream()
.mapToInt(Car::getCount)
.sum();
}
}
Cars 클래스는 생성자와 getCountCars() 외에 다른 메서드가 없다.
때문에 값을 변경/추가할 수 없다.
final은 재할당만 할 수 없을 뿐 값을 추가하는 것은 가능하기 때문에
완전한 불변 컬렉션을 만들고 싶을 경우 일급 컬렉션을 사용한다.
3. 상태와 행위를 한 곳에서 관리
일급 컬렉션의 세 번째 장점은 값과 로직이 함께 존재한다는 것이다.
예를 들어 List에 데이터를 담고, Service 혹은 Util 클래스에서 필요한 로직을 수행할 경우 문제가 발생한다.
컬렉션과 계산 로직은 서로 관계가 있는데, 위의 로직으로는 코드로 표현이 안된다.
때문에 여러 타입의 데이터를 하나의 List로 관리하고 각 타입의 상태를 구하려 한다면 각 타입별로 중복된 여러 메서드를 생성해야 한다.
즉, 특정 타입의 상태를 구하려면 계산식과 컬렉션을 함께 두어야 한다.
4. 이름이 있는 컬렉션
컬렉션에 이름을 붙일 수 있다는 것이 네 번째 장점이다.
List<drink> cola = createColaType()
List<dring> cider = createCiderType()
이렇게 음료수 리스트로 콜라와 사이다 종류를 만들면 변수명으로만 검색이 가능하며 의미를 부여하기 어렵다.
또한 중요한 값임에도 명확한 표현이 불가능하다는 단점이 있다.
이러한 경우 역시 일급 컬렉션으로 해결할 수 있다.
Cola cola = new Cola(createColaType());
Cider cider = new Cider(createCiderType());
이제 해당 컬렉션을 기반으로 용어를 사용하고 검색할 수 있다.
참고
https://jojoldu.tistory.com/412
https://velog.io/@seongwon97/%EC%9D%BC%EA%B8%89-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%B4%EB%9E%80
'Dev > Java' 카테고리의 다른 글
[Java] isPresent() vs. ifPresent() (0) | 2023.09.01 |
---|---|
[Java] pickNumberInRange ArrayIndexOutOfBoundsException (0) | 2023.08.23 |
[Java] IllegalArgumentException vs. IllegalStateException (0) | 2023.08.18 |
[Java] IntelliJ 프로젝트 빌드 안 될 때 (Link Gradle Project) (1) | 2023.08.09 |
[Java] 예외 테스트 코드가 실행되지 않을 때 (try-catch) (0) | 2023.08.07 |