동작 파라미터화 : 아직 어떻게 실행할 지 결정되지 않은 코드 블록을 의미. 나중에 프로그램에서 호출된다.
즉 실행이 나중으로 미뤄짐.
behavior를 파라미터리제이션 한 것!
자주 바뀌는 요구사항에 대응할 수 있다.
나중에 실행될 메서드 인수로 코드 블록 전달 -> 코드 블록에 따라 메서드 동작이 파라미터화 된다.
녹색 사과를 필터링 하는 코드를 빨간 사과를 필터링 하는 코드로 바꿀 때 , 메서드를 복사해 filterRedApples 만드는 것은 bad.
나중에 다른 색으로 변경 등에 적절하게 대응할 수 없다.
import java.util.ArrayList;
import java.util.List;
public class appleFilter {
public static void main(String[] args) {
List<Apple> inventory = new ArrayList<>();
Apple apple1 = new Apple();
apple1.color = Color.GREEN;
inventory.add(apple1);
Apple apple2 = new Apple();
apple2.color = Color.RED;
inventory.add(apple2);
Apple apple3 = new Apple();
apple3.color = Color.GREEN;
inventory.add(apple3);
System.out.println(filterGreenApples(inventory).toString());
}
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>(); // 사과 누적 리스트
for (Apple apple : inventory) {
System.out.println("!!");
if (Color.GREEN.equals(apple.getColor())) {
System.out.println("apple = " + apple);
result.add(apple);
}
}
return result;
}
}
class Apple {
public Color color;
public Color getColor() {
return color;
}
}
enum Color {
RED, GREEN
}
이러한 상황에서는 (비슷한 코드가 반복 존재 시) 해당 코드를 추상화 하자!
filterGreenApples 코드 반복하지 않고 어떻게 구현할 수 있을까?
-> 색을 파라미터화 하자!! (메서드에 파라미터 추가)
// 두번째 시도 (색을 파라미터화)
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
List<Apple> result = new ArrayList<>(); // 사과 누적 리스트
for (Apple apple : inventory) {
System.out.println("!!");
if (apple.getColor().equals(color)) {
System.out.println("apple = " + apple);
result.add(apple);
}
}
return result;
}
무게로 필터링을 한다고 한다면 비슷한 형태로 코드를 작성할 수 있다.
하지만 코드 대부분이 중복되고 DRY (Don't Repeat Youtself) 원칙을 위반한다.
색과 무게 필터를 합쳐 플래그로 어떤 기중으로 필터링 할 지 가리킬 수도 있지만, 매우 좋지 않은 방법!
public static List<Apple> filterApples(List<Apple> inventory, Color color, int weight, boolean flag) {...}
...
List<Apple> greenApples = filterApples(inventory, Green, 0, true);
List<Apple> heavyApples = filterApples(inventory, null, 150, false);
다음과 같은 식으로 사용해야 하는데 유연하게 대응도 어렵고 null, int, boolean 무엇 뜻하는지 알기도 어렵다.
어떻게 어떤 기준으로 필터링 할 지 효과적으로 전달할까?
-> 동적 파라미터화를 이용하자!
predicate : 참/거짓 반환하는 함수
선택 조건 결정하는 인터페이스 만들고 여러 버전의 predicate 만들어서 implement 하면 다양한 전략을 구상할 수 있다! -> 사과 선택 전략을 캡슐화 함.
조건에 따라 filter 메소드가 다르게 동작한다. 이를 전략 디자인 패턴 (Strategy Design Pattern) 이라고 부른다.
전략 디자인 패턴 : 각 알고리즘(전략) 을 캡슐화 하는 알고리즘 패밀리를 정의해두고 런타임에 알고리즘 선택하는 방법이다.
여기서는 ApplePredicate이 알고리즘 패밀리이고, AppleHeavyWeightPredicate, AppleGreenColorPredicate이 전략 이다.
동적 파라미터화 : 메소드가 다양한 동작(or 전략) 받아서 내부적으로 다양한 동작 수행 한다.
filterApples 메서드가 ApplePredicate 객체를 파라미터로 받는다.
이렇게 하면 filterApples 메서드 내부에서 컬렉션 반복하는 로직과 컬렉션의 각 요소에 적용할 동작 (여기서는 predicate) 분리할 수 있다는 점에서 sw 엔지니어링적으로 큰 이득 얻는다.
과정 간소화
현재 메소드에 새로운 동작 전달하려면 ApplePredicate 인터페이스 구현하는 여러 클래스 만든 다음에 인스턴스화 해야 한다.
어떻게 해당 과정 간소화 할 수 있을까?
자바의 익명 클래스 : 클래스 선언과 인스턴스화 동시에 수행할 수 있다.
-> 즉석에서 만들어 사용하기 가능!
익명 클래스는 local class (블록 내부에 선언된 클래스) 와 비슷한 개념.