회고
20220621-22 TIL #인터페이스 #동적 파라미터화 #일급컬렉션
paran21
2022. 6. 22. 21:57
<모던 자바 인 액션>을 읽기 시작했다.
책 자체가 굉장히 잘 쓰여져 느낌을 받았고, 편집도 좋다.
아마 최근에 관심 주제랑도 맞닿아 있어서 더 그런 것 같은데, 왜 람다를 사용해야 하는지에 대해 설득력 있게 느껴지고 있다.
특히 동적 파라미터화로 메서드를 전달할 수 있다는 점이 재미있다.
1. 인터페이스를 활용하면 메서드를 변수로 받을 수 있다.
예를 들어 List<Apple>을 색깔로 필터할 수 있다.
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if(apple.getColor().equals(color)) {
result.add(apple);
}
}
return result;
}
그런데, 여기에 새로운 필터 조건을 추가한다고 하면 새로운 메서드를 만들어야 한다.
public static List<Apple> filterApplesUnderWeight(List<Apple> inventory, int weight) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if(apple.getWeight() < weight) {
result.add(apple);
}
}
return result;
}
이러한 필터 조건들을 인터페이스로 만들고, 각각의 구현체를 만들어서 전달할 수 있다.
public interface FilterApplePredicate {
boolean test(Apple apple);
}
public class FilterAppleByColor implements FilterApplePredicate {
@Override
public boolean test(Apple apple) {
return apple.getColor().equals(Color.GREEN);
}
}
public class FilterAppleRedAndHeavyPredicate implements FilterApplePredicate {
private final int WEIGHT = 50;
@Override
public boolean test(Apple apple) {
return apple.getColor().equals(Color.RED) && apple.getWeight() > WEIGHT;
}
}
그러면 하나의 메서드로 필터할 수 있다.
public static List<Apple> filterApples(List<Apple> inventory, FilterApplePredicate p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if(p.test(apple)) {
result.add(apple);
}
}
return result;
}
2. 여러 Apple들을 하나로 관리하기 위해 일급 컬렉션 Apples를 사용할 수 있다.
public class Apples {
private final List<Apple> apples;
public Apples(Apple... apple) {
this.apples = new ArrayList<>();
Collections.addAll(apples, apple);
}
}
하나의 변수만 갖는 일급 컬렉션을 사용하면 여러가지 장점들이 언급되지만, 오늘 리펙토링하면서 느낀 장점은 모든 사과들을 빼먹지 않고 한번에 필터 메서드를 적용할 수 있다는 점이다.(https://jackjeong.tistory.com/107)
3. 스트림을 사용해서 코드를 더 간결하게 작성할 수 있다.
여기에 스트림을 사용하면 코드를 매우 간결히 줄일 수 있다!
public List<Apple> filterApples(FilterApplePredicate p) {
return apples.stream()
.filter(p::test)
.collect(Collectors.toList());
}
이런식으로 Apple을 Apples라는 객체로 생성하고, 메서드를 사용하면 된다.
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.EnumSet;
class AppleTest {
Apples apples;
@BeforeEach
void createApples() {
apples = new Apples(new Apple(Color.GREEN, 20),
new Apple(Color.RED, 60));
}
@Test
void filter_apples_with_ApplePredicate() {
Assertions.assertThat(
apples.filterApples(new FilterAppleRedAndHeavyPredicate()).size())
.isEqualTo(1);
}
@Test
void print_apples_with_Predicate() {
OutputStream out = new ByteArrayOutputStream();
System.setOut(new PrintStream(out));
apples.printApples(new PrintAppleColor());
Assertions.assertThat(out).toString().contains(Color.GREEN.name());
}
}