본문 바로가기

Dev Book Review/Java8 in Action

[자바8인액션] Chap.2 동작 파라미터화 코드 전달하기

소스코드

https://github.com/mjung1798/Jyami-Java-Lab/tree/master/java8-in-action

 

mjung1798/Jyami-Java-Lab

💻 Jyami의 Spring boot 및 Java 실험소 💻. Contribute to mjung1798/Jyami-Java-Lab development by creating an account on GitHub.

github.com

 

동작 파라미터화 : 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록, 코드 블록의 실행은 나중으로 미뤄진다.

변화하는 요구사항에 유연하게 대응할 수 있게 코드를 구현하는 방법

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

 

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

DRY(don't repeat yourself) : 같은 것을 반복하지 말것.

문자열, 정수, 불린 등의 값으로 메서드를 파라미터화 할 때 => 한줄이 아니라 메서드 전체 구현을 바꾸어야 한다.

녹색 사과 필터링 filterGreenApples(List<Apple> inventory)
-> 색에 따른 필터링 filterApplyByColor(List<Apple> inventory, String color)
-> 속성 필터링 filterApples(List<Apple> inventory, String color, int wieght, boolean flag)

별로다

 

2. 동작 파라미터화

프레디케이트 Predicate : 선택 조건을 결정하는 인터페이스 => 어떤 속성에 기초해서 불린값을 반환

전략 디자인 패턴(strategy design pattern)

  • 각 알고리즘(전략이라 불리는)을 캡슐화하는 알고리즘 패밀리를 정의해둔 다음에 런타임에 알고리즘을 선택하는 기법
  • 객체가 할 수 있는 행위를 전략으로 만들고 동적으로 행위의 수정이 필요한 경우 전략으로 바꾸는 것만으로도 행위의 수정이 가능하도록 만든 패턴
public interface ApplePredicate{
  boolean test(Apple apple); 
}

public class AppleGreenColorPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "green".equals(apple.getColor());
    }
}

public class AppleHeavyWeightPredicate implements ApplePredicate{
    @Override
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}

동작 파라미터화 : 메서드가 다양한 동작(전략)을 받아서 내부적으로 다양한 동작을 수행한다.

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

List<Apple> heavyApples = FilteringApple.filterApples(inventory, new AppleHeavyWeightPredicate());
List<Apple> greenApples = FilteringApple.filterApples(inventory, new AppleGreenColorPredicate());

내가 전달한 ApplePredicate 객체에 의해 filterApples 메서드의 동작이 결정된다.

한 메서드가 다른 동작을 수행하도록 재활용이 가능하다 : 한개의 파라미터 다양한 동작

 

3. 간소화 하기

a. 익명 클래스

익명클래스(anonymous class) 기법 : 클래스의 선언과 인스턴스화를 동시에 수행할 수 있는 기법

클래스 선언과 인스턴스화를 동시에 할 수 있다 == 즉석에서 필요한 구현을 만들어서 사용할 수 있다.

Predicate 인터페이스를 구현하는 클래스 만들어서 인스턴스 화 해야한다 (귀찮음)

List<Apple> redApples = FilteringApple.filterApples(inventory, new ApplePredicate() {
  @Override
  public boolean test(Apple apple) {
    return "red".equals(apple.getColor());
  }
});
assertThat(redApples.size()).isEqualTo(2);

그러나 장황하다 : 나쁘다!

람다 표현식으로 코드를 더 간결하게 정리할 수 있다.

b. 람다 표현식

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

유연함과 간결함 두가지를 모두 가져간다!

c. 형식 파라미터를 이용한 추상화

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

d. 종합: 동작 파라미터화의 3가지 방법

  • 클래스
  • 익명 클래스
  • 람다

 

5. 실전 예제

a. Comparator 정렬

sort 동작을 파라미터화 할 수 있다.

// 익명클래스 이용
inventory.sort(new Comparator<Apple>() {
  @Override
  public int compare(Apple o1, Apple o2) {
    return o1.getWeight().compareTo(o2.getWeight());
  }
});
// 람다식 이용
inventory.sort((a1, a2)->a1.getWeight().compareTo(a2.getWeight()));

b. Runnable 코드 블록 실행

// 익명클래스 이용
Thread t1 = new Thread(new Runnable() {
  @Override
  public void run() {
    System.out.println("helloWorld");
  }
});
t1.run();
// 람다식 이용
Thread t2 = new Thread(() -> System.out.println("helloWorld"));
t2.run();