동작 파라미터화 코드 전달하기
2장. 동작 파라미터화 코드 전달하기
- 동작 파라미터화를 이용하면 자주 바뀌는 요구사항에 효과적으로 대응할 수 있다.
- 동작이란 어떻게 실행할 것인지 결정하지 않은 코드 블록이다.
- 메서드의 인자로 코드블럭(메서드)을 전달하는 패턴
2.1 변화하는 요구사항
- 농장 재고목록 리스트에서 녹색 사과만 필터링 하는 기능을 추가한다고 가정하자.
2.1.1 녹색 사과 필터링
public enum Color {
RED, GREEN
}
public class Apple {
Color color;
int weight;
// Constructor, Getter, Setter 생략
}
public class AppleFilter {
public static List<Apple> filterGreenApples(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (Color.GREEN.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
}
- 빨간 사과도 필터링 해달라는 요구사항이 추가된다면? 다른 색상이 추가될 가능성이 있다면?
2.1.2 색을 파라미터화
public class AppleFilter {
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (color.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
}
- 사과의 무게로 필터링 하려면?
public class AppleFilter {
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;
}
}
- 무게 필터링 코드와 색 필터링 코드에서 중복되는 코드를 제거하려면?
2.1.3 가능한 모든 속성으로 필터링
- 플래그를 사용하는 방법은 실전에서는 사용하면 안되는 방법이다.
public class AppleFilter {
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;
}
}
2.2 동작 파라미터화
- 사과의 어떤 속성에 기초해서 불리언 값을 반환하는 프레디케이트 인터페이스를 정의한다.
public interface ApplePredicate {
boolean test(Apple apple);
}
``
```java
public class AppleHeavyWeightPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
public class AppleGreenColorPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return Color.GREEN.equals(apple.getColor());
}
}
public class AppleRedAndHeavyPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return Color.RED.equals(apple.getColor()) && apple.getWeight() > 150;
}
}
public class AppleFilter {
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> redAndHeavyApples = AppleFilter.filterApples(inventory, new AppleRedAndHeavyPredicate());
2.3 복잡한 과정 간소화
- ApplePredicate 인터페이스를 구현하는 여러 클래스를 정의한 다음에 인스턴스화 한 뒤, filterApples 메소드에 전달한다.
- 익명 클래스를 이용한다면?
List<Apple> redApples = AppleFilter.filterApples(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return Color.RED.equals(apple.getColor());
}
});
- 람다식
List<Apple> redApples = AppleFilter.filterApples(inventory, (Apple apple) -> Color.RED.equals(apple.getColor()));
- 제네릭
public interface Predicate<T> {
boolean test(T t);
}
public class Filter {
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> filter = Filter.filter(inventory, (Apple apple) -> Color.RED.equals(apple.getColor()));
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = Filter.filter(numbers, (Integer i) -> i % 2 == 0);
2.4 실전 예제
- 동작 파라미터화 패턴은 동작을 캡슐화한 다음에 메서드로 전달해서 메서드의 동작을 파라미터화한다.
- 디자인 패턴으로 보면 전략 패턴이다.
2.4.1 Comparator 정렬
- 자바 8의 List의 sort 메서드의 파라미터인 Comparator 인터페이스를 구현하여 sort 메서드의 동작을 정할 수 있다.
public interface Comparator<T> {
/* a negative integer, zero, or a positive integer as the
first argument is less than, equal to, or greater than the
second.
*/
int compare(T o1, T o2);
}
inventory.sort(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getWeight() - o2.getWeight();
}
});
inventory.sort((o1, o2) -> o1.getWeight() - o2.getWeight());
2.4.2 Runnable로 코드 블록 실행
- 스레드에 실행 할 코드 블록을 지정할 수 있다.
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world");
}
});
Thread t = new Thread(() -> System.out.println("Hello world"));
Leave a comment