Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Archives
Today
Total
관리 메뉴

참새의 이야기

Chapter2. 동작 파라미터화 본문

JAVA/모던 자바 인 액션

Chapter2. 동작 파라미터화

참새짹짹! 2023. 11. 14. 11:09

변화하는 요구사항에 대응하기 위해 동작 파라미터화를 사용할 수 있다.

하지만, 동작 파라미터화는 코드가 길어지게 한다.

자바 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