동작 파라미터화(behavior parameterization)를 이용하면 자주 바뀌는 요구사항에 효과적으로 대응할 수 있다.
List<Apple> filterGreenApples(List<Apple> apples){
List<Apple> result = new ArrayList<>();
for(Apple apple: apples){
if(apple.getColor().equals(GREEN)){
result.add(apple);
}
}
return result;
}
사과의 색이 GREEN이라면 result로 반환하는 상황이다.
다른 색의 결과가 받고 싶을 때는 ??
List<Apple> filterGreenApples(List<Apple> apples, Color color){
List<Apple> result = new ArrayList<>();
for(Apple apple: apples){
if(apple.getColor().equals(color)){
result.add(apple);
}
}
return result;
}
색상을 파라미터화하면 위와 같이 원하는 색으로 필터링 할 수 있다.
무게를 기준으로 필터링 하고 싶을 때는??
-> 위에서 무게를 파라미터화 해서 비슷한 구조로 구현
-> 코드가 중복된다
//인터페이스
interface Predicate{
boolean test(Apple apple);
}
//구현체
class AppleHeavyWeightPredicate implements Predicate{
@Override
boolean test(Apple apple){
return apple.getWeight()>150;
}
}
//Predicate로 필터링
List<Apple> filterGreenApples(List<Apple> apples, Predicate p){
List<Apple> result = new ArrayList<>();
for(Apple apple: apples){
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
위와 같이 전략패턴을 사용해서 필터 조건을 구체화할 수 있다.
그리고 구현체를 파라미터로 넘겨서 사용할 수 있다.
List<Apple> heavyApples =
filterApples(apples, new AppleHeavyWeightPredicate());
하지만 필터가 필요할 때마다 구현체를 만들고, 인스턴스를 생성해서 넘겨야 한다.
List<Apple> heavyApples = filterApples(apples, new Predicate(){
@Override
boolean test(Apple apple){
return apple.getWeight()>150;
}
});
익명 클래스를 사용하면 클래스 선언과 인스턴스화를 동시에 할 수 있다.
하지만 여전히 반복된 코드를 작성해야 하고, 지저분하다.
List<Apple> heavyApples =
filterApples(apples, apple -> apple.getColor()>150);
람다를 사용하면 위와 같이 간단하게 사용할 수 있다!
//java.lang.Runnable
interface Runnable{
void run();
}
Thread a = new Thread(() -> System.out.println("1번 스레드"));
Thread 클래스의 생성자에서 Runnable 클래스를 파라미터로 가진다.
위와 같이 람다식으로 run() 메서드를 전달할 수 있다.
//java.util.concurrent.Callable
interface Callable<V>{
V call();
}
executorService.submit(() -> Thread.currentThread().getName());
ExecutorService의 메서드를 사용해서 태스크를 스레드 풀로 보내고 결과를 Future에 저장할 수 있다.
이때 submit(Collable task) 메서드의 파라미터를 람다식으로 전달할 수 있다.