참새의 이야기
Chapter2. 동작 파라미터화 본문
변화하는 요구사항에 대응하기 위해 동작 파라미터화를 사용할 수 있다.
하지만, 동작 파라미터화는 코드가 길어지게 한다.
자바 8에서는 람다 표현식으로 이 문제를 해결한다.
요구사항에 대응
녹색 사과 필터링
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> apples = new ArrayList<>();
for (Apple apple:inventory) {
if(Green.equals(apple.getColor())) {
apples.add(apple)
}
}
return apples;
}
녹색 사과를 필터링하는 경우만 생각하면 위의 코드도 좋은 코드가 될 수 있다.
갑작스럽게 빨간 사과를 필터링하고 싶다는 요구 사항이 추가되면 어떻게 대응할 수 있을까?
코드를 싹 긁어서 복붙하고 일부만 수정하는 방식은 코드에 중복이 발생한다.
그렇다면 중복을 피하려면 어떻게 해야할까?
책에서는 아래의 한 문장을 눈에 띄게 덩그러니 적어뒀다.
거의 비슷한 코드가 반복 존재한다면 그 코드를 추상화한다.
색을 파라미터화
위의 문장대로 추상화를 통해 해결해 보자.
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
List<Apple> apples = new ArrayList<>();
for (Apple apple:inventory) {
if(color.equals(apple.getColor())) {
apples.add(apple)
}
}
return apples;
}
어떤 색깔이 필터링의 기준으로 들어오더라도 처리할 수 있게 하기 위해 파라미터에 Color를 추가했다.
하지만 필터링의 영역이 색깔에 갇혀있다.
무게로 필터링을 하고 싶다는 요구 사항이 들어오면 코드를 또 바꿔야 한다.
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
List<Apple> apples = new ArrayList<>();
for (Apple apple:inventory) {
if(apple.getWeight() > weight) {
apples.add(apple)
}
}
return apples;
}
이렇게 단순하게 바꾸면 두 가지 문제점이 있다.
색 필터링 메서드와 무게 필터링 메서드에 중복이 있다는 것과 이 두 기준을 조합한 필터링이 불가능하다는 것이다.
가능한 모든 속성으로 필터링
어떤 flag(어떤 기준으로 필터링할지 가리키는 코드)를 추가하는 방법이 있지만, 이 방법은 좋지 않다.
기준이 뭔지 눈에 잘 들어오지 않기 때문이다.
어떤 기준으로 필터링을 할 것인지도 파라미터로 정의하면 이 부분의 문제도 해결할 수 있다.
동작 파라미터화
Predicate
선택 조건을 결정하는 인터페이스를 정의해서 해결해 보자.
아래의 interface를 각 기준에 따라 구현하고 filter 메서드에 넘겨주면 될 것 같다.
public interface ApplePredicate {
boolean test(Apple apple);
}
우선, 초록색 사과만을 골라내는 Predicate를 구현해 보자.
public class AppleGreenColorPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return GREEN.equals(apple.getColor());
}
}
이렇게 구현한 Predicate를 아래의 filterApples에게 넘겨주면 될 것이다.
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate predicate) {
List<Apple> apples = new ArrayList<>();
for (Apple apple:inventory) {
if(predicate.test(apple)) {
apples.add(apple)
}
}
return apples;
}
ApplePredicate를 implement 하면 얼마든지 필터링의 기준을 새롭게 만들어낼 수 있다.
지금의 코드처럼 메서드가 다양한 동작을 받아서 내부적으로 다양한 동작을 수행하는 것을 동작 파라미터화라고 한다.
복수의 기준에 의한 필터링을 하고 싶다면 이제 이 방법으로 처리할 수 있다.
예를 들어, 초록색 사과이면서 무게가 170 이상인 것을 골라내고 싶다면 아래와 같은 ApplePredicate를 만들면 된다.
public class AppleGreenAndOver170Predicate implements ApplePredicate {
public boolean test(Apple apple) {
return GREEN.equals(apple.getColor()) && apple.getWeight() >= 170;
}
}
한 개의 파라미터로 다양한 동작을 할 수 있게 되었고 이게 바로 동작 파라미터화의 장점이다.
익명 클래스
지금까지의 방법으로 새로운 요구 사항에 대한 유연한 대처가 가능해졌지만, 로직과 무관한 코드가 많아졌다.
이를 개선하기 위해 익명 클래스를 사용하면 더 가독성 있는 코드를 만들 수 있다.
ApplePredicate의 구현체를 익명 클래스로 사용한 예시를 보자.
List<Apple> apples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple apple) {
return RED.equals(apple.getColor())
}
});
이마저도 여전히 길고 가독성에서의 큰 개선은 없는 것 같다.
오히려 filterApples 자체는 더 읽고 이해하기 어려워진 것 같기도 하다.
람다 표현식
이제 람다 표현식으로 해결해 보자.
List<Apple> apples = filterApples(inventory, (Apple apple) -> RED.equals(apple.getColor()));
간결, 깔끔, 명확한 한 줄의 코드로 모든 문제가 해결되었다.
'JAVA > 모던 자바 인 액션' 카테고리의 다른 글
Chapter 5. 스트림 활용 (1) | 2023.11.18 |
---|---|
Chapter 4. 스트림 소개 (1) | 2023.11.16 |
Chapter 3. 람다 표현식 (0) | 2023.11.15 |
Chapter 1. 자바의 변화 (0) | 2023.11.14 |