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


java8부터 지원하는 메서드를 코드에 파라미터로 전달하는 방법에 대해 알아보자

프로그래밍에서 젤 중요한것중 하나! 유지보수가 쉬워야됨..!! 왜냐면 요구사항이 계속 바뀌기때문.

이번 장에서 소개할 동작 파라미터화를 이용하면 자주 바뀌는 요구사항에 효과적으로 대응이 가능하다. 어떤 동작(메서드)를 파라미터화 할 수 있기때문이다.

2.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);
    }
  }
}

근데 갑자기 농부가 빨간사과 필터링해줘~! 라고 요청한다면?

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

근데 딱봐도 코드가 너무 반복.. 색을 파라미터화 해보자!

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);
    }
  }
}

이랬더니 농부가 갑자기 150그램 이상인 사과를 필터링해줘~ 라고 요청하면? 열씨미 색 필터를 만든게 소용없음. 아래와 같이 새로운 필터를 만든다…..

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;
}

List<Apple> heavyApples = filterApplesByWeight(inventory, 150);

근데 이번엔 또 색이랑 무게에 대한 조건을 다 걸어서 필터링하고싶어~ 라고 요청하면?

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;
}

List<Apple> greenApples = filterApples(inventory, GREEN, 0, true);
List<Apple> heavyApples = filterApples(inventory, null, 150, false);

와..정말 별로다! true/false는 또 뭘 의미하는건데? 노답이다.

얘를 도대체 어떻게 해야 예쁘고 간결하게 만들수있을까?

2.2 동작 파라미터화

전략 디자인 패턴을 사용해서 필요에 따른 필터를 넘겨줘서 사과를 필터링할수있게 해보자. 이때, Predicate(특정 조건에 대해 참/거짓을 반환하는 함수) 인터페이스를 사용할 수 있다.

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

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());
  }
}

오 .. 이 Predicate들을 매개변수로 넘겨받는 filter메서드를 정의하면 여러개의 중복filter를 정의하지 않고 1개만으로도 다양한 필터링을 할 수 있겠어!

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;
}

이렇게 한 메서드가 여러가지 동작(필터)를 할 수 있도록 해주는것이 동작 파라미터화의 강점이다.

2.3 복잡한 과정 간소화

1) 익명클래스 사용

Predicate를 선언하는것도 귀찮다…!! 그럼 우리는 자바의 익명클래스를 사용할 수 있다

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

근데 이것도…… 장황하고 길다.

2) 람다표현식 사용

람다표현식을 사용하면 더 간결하게 할 수 있다!

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

가독성도 훨씬 나아졌다.

3) 리스트 형식으로 추상화

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 apple) -> RED.equals(apple.getColor()));
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);

제너릭을 사용해서 추상화했더니 Apple뿐 아니라 Integer, String .. 무궁무진하게 필터링할수잇게됐다.




© 2020. by berrrrr

Powered by berrrrr