모던 자바 인 액션 2장을 학습하고 정리한 내용입니다.
동작 파라미터화
는 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미한다.
이 코드 블록은 나중에 프로그램에서 호출한다.
예를 들어 나중에 실행될 메서드의 인수로 코드 블록이 전달될 수 있다. 코드 블록에 따라 메서드의 동작이 파라미터화된다.
기존에는 goAndBuy() 메서드를 통해, '나가서 사는' 행동만 처리할 수 있었는데,
'동작 파리미터화'를 통해 동작을 파라미터처럼 사용할 수 있다.
go(Buy) // goAndBuy -> go
go(Rent) // goAndRent -> go
go(check) // goAndCheck -> go
동작 파라미터화
를 이용하면 빈번한 요구사항 변경에 효과적으로 대응할 수 있다.
Strategy pattern
적용해서 가독성, 유연성 확보
public interface ApplePredict {
boolean test (Apple apple);
}
public class AppleWeightPredict implements ApplePredict {
@Override
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
public class AppleColorPredict implements ApplePredict {
@Override
public boolean test(Apple apple) {
return apple.getColor() == Color.GREEN;
}
}
// 사용
public List<Apple> filterApples(List<Apple> apples, ApplePredict p) {
List<Apple> filteredApples = new ArrayList<>();
for (Apple apple : apples) {
if(p.test(apple)) {
filteredApples.add(apple);
}
}
return filteredApples;
}
파라미터로 전달한 ApplePredict 객체에 따라 filterApples 메서드의 작동 방식이 달라졌다.
Strategy Pattern
의 단점
List<Apple> redApples = filterApple(apples, new ApplePredict() {
@Override
public boolean test(Apple apple) {
return apple.getColor() == Color.RED;
}
})
익명 클래스를 통해 따로 클래스를 정의할 필요는 없어졌지만, 여전히 불필요한 코드는 남아있다.
자바 8의 람다 표현식을 통해 다음처럼 간단하게 표현할 수 있다.
List<Apple> result = filterApples(inventory, (Apple apple) -> app.getColor == Color.RED));
유연성 측면에서
값 파라미터화 < 클래스, 익명 클래스, 람다
코드 간결성 측면에서
값 파라미터화 < 클래스 < 익명 클래스 < 람다
제네릭을 적용해서 확장성을 향상시켜보자
public interface Predict<T> {
boolean test(T t);
}
public List<T> filter(List<T> list, Predict<T> p) {
List<T> filtered = new ArrayList<>();
for (T t : list) {
if (p.test(t)) {
filtered.add(t);
}
}
return filtered;
}
List<Apple> redApples = filter(apples, (Apple apple) -> apple.getColor() == Color.RED);
List<Integer> oddNumbers = filter(numbers, (Integer number) -> number % 2 == 1);
동작 파라미터화는 변화하는 요구사항에 쉽게 적응하는 유용한 패턴임을 알 수 있다.