[Java] 동적 파라미터화 코드 전달하기

Hyunta·2022년 3월 7일
0

모던 자바 인 액션

목록 보기
1/1

어떤 상황에서 일을 하든 소비자 요구사항은 항상 바뀐다. 우리는 보통 파라미터를 넘겨서 비교하여 계산하는 메서드를 만든다. 문제는 요구사항이 바뀔 때 마다 메서드를 오버로딩해서 새로운 파라미터를 받는 메서드를 만들어야하는 상황들이 생긴다. 하지만 동적 파라미터화 코드를 전달하면 이 상황을 타파할 수 있는데 하나씩 알아보자.


변화하는 요구사항에 대응하기

상황 : 농장 재고목록 애플리케이션에서 재고파악

요구사항 1. 녹색사과 필터링

    public static List<Apple> filterGreenApples(List<Apple> inventory) {
        ArrayList<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (Color.GREEN.equals(apple.getColor())) {
                result.add(apple);
            }
        }
        return result;
    }

보통 이런식으로 구현할 것이다, 사과의 리스트를 받아와서 색깔이 초록색과 동등할 경우 리스트에 저장해서 한번에 반환하면 초록사과 목록을 받을 수 있다. 만약 농부가 빨간 사과도 필터링을 하고 싶어진다면 메서드를 새로 만들어야할 것이다. 이를 반복하지 않으려면 색을 파라미터화해서 해결할 수 있다.

요구사항 2. 원하는 색깔로 필터링

    public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color){
        ArrayList<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (apple.getColor().equals(color)) {
                result.add(apple)
            }
        }
        return result;
    }

필터링을 할 때 사과목록과 원하는 색깔을 한번에 넘겨주면 어떤 색깔이 추가되더라도 메서드를 새로 만들 필요가 없다. 그런데 갑자기 농부가 색 이외에 무게에 대해서도 필터링을 하기를 원한다면 150그램이 기준이지만 언제든지 바뀔 수 있다면?

    public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight){
        ArrayList<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (apple.getWeight() > weight) {
                result.add(apple);
            }
        }
        return result;
    }

그렇다면 색깔을 필터링한 것 처럼 무게를 필터링해서 처리할 수 있다. 하지만 이 두 코드는 대부분 중복된다. 소프트웨어 공학의 DRY(Don't Repeat Yourself) 원칙을 어기는 것이다. 어떻게하면 속성에 관계없이 필터링할 수 있는 메서드를 만들 수 있을까?

요구사항3. 무게 혹은 색깔로 필터링

동적 파라미터화
우리의 선택 조건을 애초에 어떤 속성에 기초해서 boolean값을 반환하는 방법이 있다. Predicate를 이용해 선택 조건을 결정하는 인터페이스를 정의하자. 전략패턴을 이용하여 원하는 전략을 이용해 필터링할 수 있다.

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

전략패턴을 이용해 우리는 첫번째 코드보다 유연해졌고, 농부의 요구사항에 맞는 전략 구현체를 이용해서 대처할 수 있게 됐다.

    public static void main(String[] args) {
        List<Apple> inventory = Arrays.asList(
                new Apple(GREEN, 180),
                new Apple(RED, 70));
        List<Apple> apples = AppleFilter.filterApples(inventory, new AppleHeavyWeightPredicate());
        System.out.println(apples);
    }
}

ApplePredicate 를 이용해서 구현체를 이용하면 위에 식과 같이 사용할 수 있다. 하지만 구현체를 일일히 만들어서 비교하려면 코드도 길어지고 시간도 많이 걸린다. 그래서 람다 표현식을 이용한다면 아래와 같은 코드로 작성할 수 있다.

    public static void main(String[] args) {
        List<Apple> inventory = Arrays.asList(
                new Apple(GREEN, 180),
                new Apple(RED, 70));
        
        List<Apple> apples = AppleFilter.filterApples(inventory, (Apple apple) -> RED.equals(apple.getColor()));
        System.out.println(apples);
    }

람다 표현식을 이용하면 간결하게 문제를 해결할 수 있다.
여기서 리스트마저 추상화한다면 사과뿐만 아니라 다양한 형태에 대해서도 필터링할 수 있게된다.

    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T e : list) {
            if (predicate.test(e)) {
                result.add(e);
            }
        }
        return result;
    }
    
        List<Integer> numbers = IntStream.range(0, 10).boxed().collect(Collectors.toList());
        List<Integer> evenNumbers = FruitFilter.filter(numbers, (Integer i) -> i % 2 == 0);
    

우리가 원하는 조건을 찾을 때 동적 파라미터화 코드를 통해 유연하고 간결한 코드를 작성할 수 있게된다. 이번에 로또 당첨갯수를 찾을 때 이를 이용해서 구현했다.

Reference

모던 자바 인 액션 2장 - 동작 파라미터화 코드 전달하기

profile
세상을 아름답게!

0개의 댓글