람다란 무엇인가 - 01. 동작 파라미터화

변화하는 요구사항에 유연하게 대응하기 위해 동작 파라미터화가 필요하다.

동작 파라미터란 어떻게 실행할 것인지 결정되지 않은 코드 블록을 의미한다. 실행될 메서드의 인수로 동작 파라미터로 전달하고 나중에 프로그램에서 호출한다.

동작 파라미터가 필요한 과정을 소스를 통해 확인해 보자.

사과를 색으로 필터링하는 요구 사항이 있다고 가정한다.

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> filterApplesByWeight(
    List<Apple> inventory, int weight        
) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple: inventory) {
        if (apple.getWeight() > weight) {
            result.add(apple);
        }
    }
    return result;
}

다양한 조건을 기준으로 필터링하는 요구 사항은 계속 추가될 것이다.

위의 코드를 반복할 것인가? 이는 소프트웨어 공학의 DRY(don’t repeat yourself) 원칙을 어기는 것이다.

동작 파라미터화로 유연한 코드를 만들 수 있다.

동작을 구현하기 위한 인터페이스를 정의한다.

public interface ApplePredicate {
    boolean test(Apple apple);
}

Predicate : 참 또는 거짓을 반환하는 함수를 의미한다.

다양한 선택 조건의 ApplePredicate를 정의한다.

public class AppleHeavyWeightPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}
public class AppleGreenColorPredicate implements ApplePredicate {
    public boolean test(Apple apple) {
        return GREEN.equals(apple.getColor());
    }
}

동작을 파라미터화한 필터 메서드를 구현한다.

public static List<Apple> filterApples(
        List<Apple> inventory, 
        ApplePredicate p
) {
    List<Apple> result = new ArrayList<>();
    for(Aople apple: inventory) {
        if(p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

구현한 필터 메서드와 ApplePredicate를 통해 원하는 조건으로 필터링할 수 있다.

List<Apple> haevyApples = filterApples(inventory, new AppleHeavyWeightPredicate());

그런데 선택 조건이 추가될 때마다 새로운 ApplePredicate를 구현해야 할까?
다음 장에서 익명 클래스와 람다로 코드를 개선해 보자.