[Java] Chapter 2. 동작 파라미터화 코드 전달하기

SeungWoo Cha·2025년 9월 24일

[Java] 자바 인 액션

목록 보기
1/2

Chapter 2. 동작 파라미터화 코드 전달하기

2.0. 서론

  • 동작 파라미터화(Behavior Parameterization)
    : 아직 실행 방법을 결정하지 않은 코드 블록을 의미한다.
  • 이 코드 블록은 나중에 프로그램에서 호출되어 실행을 지연시킬 수 있다.

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

2.1.1. 첫 번째 시도 : 녹색 사과 필터링

public static List<Apple> filterGreenApples(List<Apple> inventory) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ("green".equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}
  • 단순히 if 조건문으로 필터링.
  • 그러나 요구사항이 변하면(예: 색상별 필터링, 무게 조건 추가 등) 쉽게 대응 불가.

2.1.2. 두 번째 시도 : 색을 파라미터화

public static List<Apple> filterApplesByColor(List<Apple> inventory, String color) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (color.equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}
  • **색상(color)**을 매개변수로 전달 → 다양한 색상 필터 가능.
  • 하지만 무게 기준 등 다른 속성을 필터링할 때는 또 다른 메서드가 필요 → 코드 중복 발생 (DRY 원칙 위배).

2.1.3. 세 번째 시도 : 모든 속성을 조건으로

public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ((flag && color.equals(apple.getColor())) ||
            (!flag && apple.getWeight() > weight)) {
            result.add(apple);
        }
    }
    return result;
}
  • 하나의 거대한 필터 메서드를 만들어 모든 조건 처리.
  • 하지만 결국 중복 메서드 또는 복잡한 하나의 메서드 → 유지보수 어려움.
  • 해결책: 동작 파라미터화.

2.2. 동작 파라미터화

2.2.0. 서론

  • 단순히 매개변수를 늘리는 방식이 아닌, 요구사항 변화에 유연하게 대응할 방법 필요.

  • Predicate 인터페이스

    • 조건을 추상화 → true / false 반환.
  • 전략 디자인 패턴

    • 알고리즘(전략)을 캡슐화하고, 런타임에 선택 가능.
  • 따라서, 메서드는 다양한 동작(전략)을 인수로 받아 내부적으로 실행 가능해야 함.


2.2.1. 네 번째 시도 : 추상적 조건으로 필터링

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

public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}
  • **인터페이스(ApplePredicate)**를 정의하고, 이를 구현하는 객체를 전달.
  • 메서드의 조건을 외부에서 결정 → 다양한 동작을 전달 가능.
  • 즉, 코드 자체를 전달할 수 있게 됨.

2.3. 복잡한 과정 간소화

2.3.0. 서론

  • 매번 클래스를 구현 → 인스턴스 생성은 복잡하고 번거로움.
  • 자바는 이를 간단히 하기 위해 익명 클래스를 제공.

2.3.1. 익명 클래스

List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
    @Override
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor());
    }
});
  • 클래스 선언 + 인스턴스 생성을 동시에 처리.
  • 하지만 코드가 여전히 장황하고 가독성이 떨어짐.

2.3.2. 다섯 번째 시도 : 람다 표현식

List<Apple> redApples = filterApples(inventory, apple -> "red".equals(apple.getColor()));
  • 자바 8부터는 람다 표현식 지원 → 코드 간결화.

2.3.3. 여섯 번째 시도 : 리스트 형식으로 추상화

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;
}
  • 제네릭 메서드 적용 → 다양한 객체 타입에 대해 사용 가능.

2.4. 실전 예제

2.4.1. Comparator로 정렬하기

inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
  • 요구사항에 맞는 Comparator를 전달 → 정렬 동작 유연화.

2.4.2. Runnable로 코드 블록 실행하기

Thread t = new Thread(() -> System.out.println("Hello from another thread!"));
t.start();
  • Runnable에 람다 전달 → 스레드 실행 단순화.

2.4.3. GUI 이벤트 처리하기

ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Integer> result = executor.submit(() -> 42);
  • ExecutorService + Callable

    • 테스크 제출과 실행 과정 분리.
    • 결과값을 비동기적으로 저장 가능.

2.5. 마치며

  1. 동작 파라미터화 → 메서드 인수로 다양한 동작을 전달할 수 있음.
  2. 변화하는 요구사항에 더 잘 대응 → 유지보수 비용 절감.
  3. 자바 8 이전: 익명 클래스 사용 → 코드 장황.
    자바 8 이후: 람다 표현식으로 간결화.
  4. 자바 API는 정렬, 스레드, 이벤트 처리 등에서 동작 파라미터화를 폭넓게 활용.
profile
한 발자국씩

0개의 댓글