Written by
Poogle
on
on
Modern Java in Action - Ch.2
참고: 책 - Modern Java in Action
Ch 2. 동작 파라미터화 코드 전달하기
- 자주 바뀌는 요구사항에 효과적으로 대응
- 아직 어떻게 실행할 것인지 결정하지 않은 코드 블록
- 나중에 프로그램에서 호출 -> 코드블록의 실행이 나중으로 미뤄짐
변화하는 요구사항에 대응하기
- ex. 무게와 색깔을 속성으로 갖는 사과 -> 녹색 사과 중 무거운 사과를 필터링 하고 싶다.
- 모든 속성을 메서드 파라미터로 추가한 더러운 코드
- Apple
package com.programmers.java.modern.ch2;
public class Apple {
private Color color;
private int weight;
public Apple(Color color) {
this.color = color;
}
public Color getColor() {
return color;
}
public int getWeight() {
return weight;
}
}
- Color
package com.programmers.java.modern.ch2;
public enum Color {
RED, GREEN;
}
- 모든 것을 처리하는 거대한 하나의 필터 메서드
public static List<Apple> filterApplesBad(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; }
디자인 패턴 활용
-
사과의 어떤 속성에 기초해서 boolean값을 반환하도록 하는 방법 -> predicate 함수를 이용, 선택조건을 결정하는 인터페이스 정의
-
ApplePredicate 인터페이스, AppleGreenColorPredicate, AppleHeavyWeightPredicate, AppleRedAnd HeavyPredicate
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 class AppleRedAndHeavyPredicate implements ApplePredicate {
@Override
public boolean test(Apple apple) {
return RED.equals(apple.getColor()) && apple.getWeight() > 150;
}
}
- 전략 디자인 패턴
- 캡슐화하는 알고리즘 패밀리 정의
- 런타임 시 알고리즘 선택
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate predicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (predicate.test(apple)) {
result.add(apple);
}
}
return result;
}
- 컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 있음
- 여러 클래스를 구현해서 인스턴스화 하는 과정이 조금 거추장 스럽다고 느낀다면?
익명 클래스
- 클래스 선언과 인스턴스화를 동시에
//익명 클래스 버젼 List<Apple> redApples = filterApples(inventory, new ApplePredicate() { @Override public boolean test(Apple apple) { return RED.equals(apple.getColor()); } });
- filterApples()의 동작을 직접 파라미터화
참고 - 익명클래스의 장황함
public class MeaningOfThis {
public final int value = 4;
public void doIt() {
int value = 6;
Runnable r = new Runnable() {
public final int value = 5;
@Override
public void run() {
int value = 10;
System.out.println(this.value);
}
};
r.run();
}
}
public static void main(String[] args) {
MeaningOfThis m = new MeaningOfThis();
m.doIt();
// 5
}
- 코드에서 this는 MeaningOfThis가 아니라 Runnable을 참조해서 5가 정답
- 한 눈에 알아보기 쉽지 않음
람다 사용
List<Apple> result = filterApples(inventory, (Apple apple) -> RED.equals(apple.getColor()));
- 유연함과 간경함 모두 실현 가능
- 변화하는 요구사항에 쉽게 적응하는 유용한 패턴임을 알 수 있다.
- 동작 파라미터 패턴은 동작을 (한 조각의 코드로) 캡슐화한 다음에 메서드로 전달해서 메서드의 동작을 파라미터화한다.