☕️ java
일급 컬렉션
beomsic
2023. 10. 24. 02:56
🧑🏻💻 이동욱님의 글을 보고 공부용으로 정리했습니다.
⭐ 일급 컬렉션
Collection을 Wrapping 하면서, 그 외 다른 멤버 변수가 없는 상태를 일급 컬렉션이라 한다.
Map<String, String> map = new HashMap<>();
map.put("1", "A");
map.put("2", "B");
map.put("3", "C");
public class GameRanking {
private Map<String, String> ranks;
public GameRanking(Map<String, String> ranks) {
this.ranks = ranks;
}
}
🌟 Wrapping의 이점
- 비지니스에 종속적인 자료구조
- Collection의 불변성 보장
- 상태와 행위를 한 곳에서 관리
- 이름이 있는 컬렉션
1️⃣ 비지니스에 종속적인 자료구조
📖 숫자 야구의 2가지 조건
- 3개의 번호로 이루어져있다.
- 3개의 번호는 1 ~ 9 까지의 서로 다른 임의의 수이다.
🧑🏻💻 구현
public class BaseBallApp {
private static final int DIGIT = 3;
...
if(!InputValidator.isValidatedAnswer(DIGIT, inputAnswer)) {
throw new IllegalArgumentException(
String.format("%d자리 숫자가 아닌 잘못된 입력값 입니다. - %s", DIGIT, inputAnswer));
}
...
}
- 비지니스 로직을 서비스 부분에서 처리
⚠️ 문제
숫자 야구 번호가 필요한 곳에서 계속해서 검증로직이 들어가야 한다.
- 모든 코드에 대해 알고 있지 않다면 문제가 발생할 수 있다.
💡 숫자 야구의 조건을 만족하는 자료구조를 직접 만들자.
⇒ 해당 조건으로만 생성할 수 있는 자료구조
public class BaseBallNumber {
private static final int DIGIT = 3;
private final int[] numbers;
public BaseBallNumber(int[] numbers) {
validationSize(DIGIT, numbers);
validationDuplicated(numbers);
this.numbers = numbers;
}
...
}
- 이런 클래스를 일급 컬렉션이라고 한다.
2️⃣ 불변
일급 컬렉션은 컬렉션의 불변을 보장한다.
📖 final 키워드
- Java 에서의 final 은 불변을 만들어주는 것이 아니다 ❌, 재할당만 금지한다
🌟 불변 객체의 중요성
- 객체의 값이 절대 바뀔일이 없다는 것이 보장되면 코드를 이해하고 수정하는 사이드 이펙트가 최소화된다.
Java에서는 final로 불변 객체를 만들 수 없기 때문에 일급 컬렉션과 래퍼 클래스등의 방법으로 해결해야 한다.
public class BaseBallNumber {
private static final int DIGIT = 3;
private final int[] numbers;
public BaseBallNumber(int[] numbers) {
validateSize(numbers);
validateWithinRange(numbers);
this.numbers = numbers;
}
public void calculate(BiConsumer<Integer,Integer> biConsumer){
for(int i = 0; i < numbers.length; i++) {
biConsumer.accept(numbers[i], i);
}
}
}
이렇게 값을 변경할 수 있는 메소드가 없는 컬렉션을 만들게 되면 해당 컬렉션은 불변 컬렉션이 된다.
3️⃣ 상태와 행위를 한 곳에서 관리
- 값과 로직이 함께 존재
ex) 여러 Pay 방법들이 있을 때, NaverPay 금액의 합이 필요한 경우
List<Pay> pays = Arrays.asList(
new Pay(NAVER_PAY, 1000),
new Pay(KAKAO_PAY, 1500),
new Pay(NAVER_PAY, 2000),
new Pay(KAKAO_PAY, 2000));
Long naverPaySum = pays.stream()
.filter(pay -> pay.getPayType().equals(NAVER_PAY))
.mapToLong(Pay::getAmount)
.sum();
- Pay라는 컬렉션과 계산하는 로직에 관계가 존재한다.
⚠️ 문제
- 똑같은 기능을 하는 메소드가 중복되어 생성될 수 있다.
- 메소드를 누락할 수 있다.
- 다른 결과가 필요한 경우 더 코드가 흩어질 수 있다.
public class PayGroups {
private List<Pay> pays;
public PayGroups(List<Pay> pays) {
this.pays = pays;
}
public Long getNaverPaySum() {
return pays.stream()
.filter(pay -> PayType.isNaverPay(pay.getPayType()))
.mapToLong(Pay::getAmount)
.sum();
}
public Long getKakaoPaySum() {
return getFilteredPays(pay -> PayType.isKakaoPay(pay.getPayType()));
}
private Long getFilteredPays(Predicate<Pay> predicate) {
return pays.stream()
.filter(predicate)
.mapToLong(Pay::getAmount)
.sum();
}
}
- PayGroups 라는 일급 컬렉션을 통해 상태와 로직이 한곳에서 관리될 수 있게 되었다.
List<Pay> pays = Arrays.asList(
new Pay(NAVER_PAY, 1000),
new Pay(KAKAO_PAY, 1500),
new Pay(NAVER_PAY, 2000),
new Pay(KAKAO_PAY, 2000));
PayGroups payGroups = new PayGroups(pays);
Long naverPaySum = payGroups.getNaverPaySum();
4️⃣ 이름있는 컬렉션
컬렉션에 이름을 붙일 수 있다.
- 일급 컬렉션을 만들면 해당 컬렉션을 기반으로 용어 사용과 검색을 하면 된다.