Home 동작 파라미터화 코드 전달하기
Post
Cancel

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

동작 파라미터화란?

아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미한다.

  • 특징 :
    • 리스트의 모든 요소에 대해서 ‘어떤 동작’을 수행할 수 있음
    • 리스트 관련 작업을 끝낸 다음에 ‘어떤 다른 동작’을 수행할 수 있음
    • 에러가 발생하면 ‘정해진 어떤 다른 동작’을 수행할 수 있음

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

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

1
2
3
4
5
6
7
8
9
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;
  }
  }

Q. 만약 요구사항이 변경되어 다른 색으로 필터링을 해야할 경우 위의 코드는 즉각 대응할 수 있을까?

A. 불가능하다. 위의 메서드에 파라미터를 추가하여 변화하는 요구사항에 좀 더 유연하게 대응하는 코드로 변경 해야한다.

두 번쟤 시도 : 색을 파라미터화

1
2
3
4
5
6
7
8
9
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;
  }
  }

Q. 만약 색 이외에도 가벼운 사과와 무거운 사과를 구분하도록 요구사항이 변경된다면?

A. 위와 같이 파라미터를 변경하고 조건문도 요구사항에 맞게 수정하면 된다.

1
2
3
4
5
6
7
8
9
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 - 같은 것을 반복하지 말 것)원칙을 어기는 것이다.
  • 중복을 제거하기위해 합치는 방법도 있다.(이방법은 실전에서는 절대 이 방법을 사용하면 안된다.)

세 번째 시도 : 가능한 모든 속성으로 필터링

1
2
3
4
5
6
7
8
9
public static List<Apple> filterApples(List<Apple> inventory,Color color,int weight,boolean flag){
  List<Apple> result=new ArrayList<>();
  for(Apple apple:inventory){
  if((flag&&apple.getColor().equals(color))||(!flag&&apple.getWeight()>weight)){
  result.add(apple);
  }
  }
  return result;
  }
  • 추가하여 중복은 최소화 할 수 있지만 코드가 난잡하다.
  • 요구사항이 변경될 경우 유연하게 대응하는 것도 불가능하다.

동작 파라미터화

  • 위의 예제를 통해 요구사항에 더 유연하게 대응할 수 있는 방법으로 동작 파라미터화가 있다.

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

  • 참 또는 거짓을 반환하는 함수 (predicate) 인터페이스를 생성한다.
1
2
3
4
public interface ApplePredicate {

  boolean test(Apple apple);
}

선택 조건을 대표하는 여러 버전의 ApplePredicate 를 정의할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
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());
  }
}

  • 위 조건에 따라 filter 메서드가 다르게 동작할 것이라고 예상할 수 있다. 이를 전략 디자인 패턴이라고 한다.

Q. ApplePredicate는 어떻게 다양한 동작을 수행할 수 있을까?

A. filterApples에서 ApplePredicate 객체를 받아 애플의 조건을 검사하도록 메서드를 수정해야 한다. 이를 동작 파라미터화 라고 한다.

1
2
3
4
5
6
7
8
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);
  }
  }
  }
  • 이렇게 설계할 경우 요구사항이 추가되었을때 ApplePredicate를 적절하게 구현하는 클래스를 만들기만 하면된다.

복잡한 과정 간소화

  • 유연성과 가독성은 챙길 수 있었지만 매번 여러 클래스를 정의하고 인스턴스화해야 한다.
  • 이는 익명 클래스를 통해 해결할 수 있다.

Q. 익명 클래스란?

A. 자바의 지역 클래스와 비슷한 개념이다. 익명 클래스는 말 그대로 이름이 없는 클래스로 클래스 선언과 인스턴스화를 동시에 할 수 있다.

다섯 번째 시도 : 익명 클래스 사용

1
2
3
4
5
List<Apple> redApples=filterApples(inventory,new ApplePredicate()){
public boolean test(Apple apple){
  return RED.equals(apple.getColor());
  }
  }
  • 익명 클래스로도 아직 부족한 점이 있다.
    • 1 : 익명 클래스는 여전히 많은 공간을 차지한다.
    • 2 : 많은 프로그래머가 익명 클래스의 사용에 익숙하지 않다.

여섯 번째 시도 : 람다 표현식 사용

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

일곱 번째 시도 : 리스트 형식으로 추상화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)) {
        reuslt.add(e);
      }
    }
    return result;
  }
  • 이렇게 설계할 경우 사과 이외 바나나, 정수, 문자열 등 리스트에 필터 메서드를 사용할 수 있다.
This post is licensed under CC BY 4.0 by the author.