Language/Java

[모던 자바 인 액션] Ch.2 - 동작 파라미터화

비소_ 2022. 6. 8. 17:19

클릭하시면 네이버 Book으로 연결됩니다!

해당 서적을 참고하여 개인 공부용으로 정리한 글입니다.

기본적인 Java 지식이 있으시다면 이해하기 수월한 정도입니다 :)

또한, 해당 책은 2018년 Java 11 기준으로 작성되어 있습니다.

따라서 이후 버전에서 변경된 내용은 수정하여 작성했습니다. (아는 부분만 말입니다..)


1. 동작 파라미터화

시시각각 변화하는 사용자 요구사항에 대응하기 위해 동작 파라미터화를 한다.

동작 파라미터화 : 어떻게 실행할 것인지 결정하지 않은 코드 블록

이 코드 블록은 나중에 프로그램에서 호출한다. 즉, 실행이 나중으로 미뤄진다.

기존에는 동작 파리미터화를 추가하려면 불필요한 코드가 늘어났지만, 자바 8에서는 람다식으로 해결했다.

 

필터링의 속성들을 전부 파라미터에 추가하면 초반 2~3개는 그럭저럭 견딜만 하지만, 그 이후부터는 답이 없다.

크기, 무게, 색상, 출하지 등등.. 파라미터 개수가 많아지면 하나하나 다 만들어줘야 할 것이다.

따라서, 공통 동작을 중심으로 하나의 메서드로 만들고 조건을 파라미터화 한다.

 

Predicate를 통해 선택 조건을 결정하는 인터페이스를 정의하고

이를 구현한 클래스들을 통해 전략 패턴(Stratrgy Pattern)을 만들 수 있다.

이 방법 역시 코드를 전달하는 동작 파라미터화이지만, 메서드를 구현하는 객체를 감싸야하고, 클래스를 모두 정의해줘야 한다.

 

컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 있다는 것이 동작 파라미터화의 강점이다.

public static void prettyPrintApple(List<Apple> inventory, ApplePredicate p) {
    for (Apple apple : inventory) {
        String output = p.print(apple);
        System.out.println(output);
    }
}

public interface ApplePredicate {
    String print(Apple apple);
}

public class FirstApplePredicate implements ApplePredicate {
    public String print(Apple apple) {
        return "PERFECT APPLE : " + apple.getType();
    }
}

...many things

prettyPrintApple(inventory, new FirstApplePredicate());

이처럼 여러 클래스를 정의하고 인스턴스화하는 것은 상당히 번거로운 작업이다.

2. 복잡한 과정 간소화

우선 클래스의 선언과 인스턴스화를 동시에 수행할 수 있는 익명 클래스를 활용해 해결해본다.

List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
	public boolean test(Apple apple) {
        return RED.equals(apple.getColor());
	}
});

하지만 여전히 로직과 상관없는 코드들이 많다.

또한 코드를 전달하는 과정에서 결국 객체를 만들고 명시적으로 새로운 동작을 정의하는 메서드를 구현해야 한다는 점은 변하지 않는다.

 

람다식을 이용하면 다음처럼 재구현할 수 있다.

List<Apple> redApples = filterApples(inventory, apple -> RED.equals(apple.getColor()));

이제는 사과뿐만 아니라 바나나, 오렌지 등에 대해서도 필터 메서드를 사용할 수 있도록 추상화한다.

//Predicate는 Java 표준 라이브러리다.
public interface Predicate<T> {
    boolean test(T t);
}

public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

List<Apple> redApples = filter(inventory, apple -> RED.equals(apple.getColor()));
List<Apple> yellowBananas = filter(inventory, banana -> YELLOW.equals(banana.getColor()));

결론

  • 메서드 내부적으로 다양한 동작을 수행할 수 있도록 동작 파라미터화한다.
  • 람다식을 이용한 동작 파라미터화는 간결하고 유연하다.