1. 서론
이번 포스팅에서는 Chapter2의 동작 파라미터화 코드 전달하기 에 대해 진행하도록 하겠습니다.
진행하기에 앞서 동작 파라미터화의 의미를 설명하자면 아래와 같습니다.
아직은 어떻게 실행할것인지 결정하지 않은 코드 블록
2. 변화하는 요구사항에 대응하기
책에서는 사과 농부의 요구사항을 예로 들고 있습니다.
요구사항으로는 녹색인 사과 혹은 특정 무게 이상인 사과와 같이 filter 류의 요구사항들입니다.
1) 첫 번째 시도 : 녹색 사과 필터링
private enum Color {
GREEN,
RED
}
@Getter
@RequiredArgsConstructor
private static class Apple {
private final Color color;
private final int weight;
}
private static List<Apple> filterGreenApples(List<Apple> apples) {
List<Apple> result = new ArrayList<>();
for(Apple apple: apples) {
if(Color.GREEN.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
아직, 색이 다양해짐에 따라 메소드들이 늘어나야 하는 불편함이 존재합니다.
아래는 이를 해결하기 위해 값을 파라미터화한 케이스입니다.
2) 두 번째 시도 : 색을 파라미터화
private enum Color {
GREEN,
RED
}
@Getter
@RequiredArgsConstructor
private static class Apple {
private final Color color;
private final int weight;
}
private static List<Apple> filterApplesByColor(List<Apple> apples, Color color) {
List<Apple> result = new ArrayList<>();
for(Apple apple: apples) {
if(color.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
private static List<Apple> filterApplesByWeight(List<Apple> apples, int weight) {
List<Apple> result = new ArrayList<>();
for(Apple apple: apples) {
if(apple.getWeight() > weight) {
result.add(apple);
}
}
return result;
}
값을 파라미터화한 이 코드가 나쁘진 않습니다. 다만, 중복되는 코드가 존재하는것을 볼 수 있습니다.
아래는 그것을 해결하기 위해 flag 값을 두어 필터링하는 케이스입니다.
3) 세 번째 시도 : 가능한 모든 속성으로 필터링
private static List<Apple> filterApples(List<Apple> apples, Color color, int weight, boolean flag) {
List<Apple> result = new ArrayList<>();
for(Apple apple: apples) {
if((flag && apple.getColor().equals(color))
|| (!flag && apple.getWeight() > weight)) {
result.add(apple);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> appleList = Arrays.asList(new Apple(Color.RED, 200), new Apple(Color.GREEN, 30), new Apple(Color.GREEN, 300));
final List<Apple> greenAppleList = filterApples(appleList, Color.GREEN, 0, true);
final List<Apple> heavyAppleList = filterApples(appleList, null, 150, false);
}
한개의 메서드로 줄어지긴 했지만 코드가 너무 지저분하며 이해하기가 어렵습니다.
이를 해결하기 위해, 이제 동작 파라미터화를 진행해보겠습니다.
3. 동작 파라미터화
private interface ApplePredicate {
boolean test (Apple apple);
}
private static class AppleHeavyWeightPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
private static class AppleGreenColorPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return Color.GREEN.equals(apple.getColor());
}
}
위와 같이 인터페이스를 하나 두어 각 필터링을 담당하는 클래스들을 선언합니다.
1) 네 번째 시도 : 추상적 조건으로 필터링
private static List<Apple> filterApples(List<Apple> apples, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for(Apple apple: apples) {
if(p.test(apple)) {
result.add(apple);
}
}
return result;
}
동작 파라미터화로 지저분한 코드를 깔끔하게 만들었으며 이해하기도 쉬워졌습니다.
다만, 요구사항이 늘어남에 따라 클래스를 생성해야 하니 코드수가 많이 늘게 됩니다.
또한, 한번만 사용하는 소스도 굳이 클래스를 생성하여 써야하는 불편함이 생겼습니다.
이를 해결하기 위해 아래와 같이 간소화 작업을 진행하겠습니다.
4. 복잡한 과정 간소화
1) 다섯 번째 시도 : 익명 클래스 사용
private static List<Apple> filterApples(List<Apple> apples, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for(Apple apple: apples) {
if(p.test(apple)) {
result.add(apple);
}
}
return result;
}
final List<Apple> redAppleList = filterApples(appleList, new ApplePredicate() {
public boolean test(Apple apple) {
return Color.RED.equals(apple.getColor());
}
});
익명클래스를 통해 클래스 선언을 제거했습니다.
하지만, 위 두번째 시도인 색을 파라미터화와 같이 중복되는 코드들이 많게됩니다.
2) 여섯 번째 시도 : 람다 표현식 사용
final List<Apple> redAppleList = filterApples(appleList, apple -> Color.RED.equals(apple.getColor()));
자바 8의 기능인 람다를 사용하여 중복되는 코드를 줄였으며 이해하기도 편하게 되었습니다.
5. 마무리
이번 포스팅에서는 동작 파라미터화 코드 전달하기에 대해 간단한 예제를 진행해보았습니다.
다음에는 람다 표현식에 대해 포스팅하겠습니다.
'Programming > ModernJavaInAction' 카테고리의 다른 글
(6) 스트림으로 데이터 수집 (0) | 2020.04.04 |
---|---|
(5) 스트림 활용 (0) | 2020.04.04 |
(4) 스트림 소개 (0) | 2020.03.28 |
(3) 람다 표현식 (0) | 2020.03.28 |
(1) 자바 8, 9, 10, 11 : 무슨 일이 일어나고 있는가? (0) | 2020.03.21 |