아직은 어떻게 실행할 것인지 결정되지 않은 코드 블록.
- 장점 : 한 메소드가 여러 동작을 수행할 수 있도록 함, 동작을 로직과 분리하여 유연한 API를 만들 수 있다.
👩🌾 기획자 : 녹색 사과만 필터링 → 빨간 사과도 필터링
enum Color = { RED , GREEN, YELLOW }
// 🔥
public static List<Apple> filterAppleByColor(List<Apple> inventory, Color color){
List<Apple> result = new ArrayList<>;
for (Apple apple: inventory){
if (apple.getColor().equals(color){
return result.add(apple);
}
}
}
👩🌾 기획자 : 색 외에도 무게도 필터링해주셨으면 좋겠습니다.
👩💻 개발자: (미리미리 좀 말하란 말이야....무게 정보 필터 메소드도 하나 만들어야겠다.)
public static List<Apple> filterAppleByWeight(List<Apple> inventory, int weight){
List<Apple> result = new ArrayList<>;
for (Apple apple: inventory){
if (apple.getWeight() > weight){
return result.add(apple);
}
}
}
⇒ 이러고 나니 색 필터링과 무게 필터링 코드가 한 줄 빼고 다 비슷해졌다. 들여다보고 있자니 굉장히 불편해지는 상황이다.
boolean 값을 반환하는 함수를 Predicate
라고 한다. 선택 조건을 결정하는 인터페이스를 하나 정의해보자.
public interface ApplePredicate{
boolean condition(Apple apple);
}
그리고 색과 무게를 체크하는 클래스를 각각 만들어본다.
// 1. 무거운 사과만 선택
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean condition(Apple apple){
return apple.getWeight() > 150;
}
}
// 2. 초록색 사과만 선택
public class AppleGreenColorPredicate implements ApplePredicate{
public boolean condition(Apple apple){
return GREEN.equals(apple.getColor);
}
}
전략 디자인 패턴 : 패턴화하는 알고리즘 패밀리를 정의해놓고, 런타임 시에 선택하여 사용한다.
→ 이제 메소드가 다양한 동작을 받아서 내부적으로 수행할 수 있게 되었고, 예제를 보면 predicate 객체로 사과 검색 조건을 캡슐화하였다.
즉, 넘겨주는 ApplePredicate 객체에 따라 filtersApple 메소드의 "동작"이 결정된다. 즉, 동작을 "파라미터화" 한 것이다.
// 🔥v2
public static List<Apple> filterAppleByWeight(List<Apple> inventory, ApplePredicate p){
List<Apple> result = new ArrayList<>;
for (Apple apple: inventory){
if (p.condition(apple)){
return result.add(pple);
}
}
}
메서드는 객체만 인수로 받기 때문에 condition 메소드를 ApplePredicate로 감싸서 전달한 것이다.
여러 클래스들을 생성하고 인스턴스화하는 과정이 조금 귀찮게 느껴질 수 있다. 이 부분을 개선하기 위해 익명 클래스가 필요하다.
익명 클래스
: 클래스 선언과 인스턴스화를 동시에 수행할 수 있음
List<Apple> redApples = filterApples(inventory, new ApplePredicate(){
public boolean condition(Apple apple){
return RED.equals(apple.getColor());
}
}
List<Apple> result = filterApples(inventory,
(Apple apple) -> RED.equals(apple.getColor)));
혹은 리스트 형식으로 추상화하여 사과 말고도 바나나, 오렌지, 문자열 등 다양한 타입의 리스트에도 쓸 수 있다.
public interface Predicate<T>{
boolean condition(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> result = new ArrayList<>();
for(T e: list){
if (p.condition(e){
result.add(e);
}
}
return result;
}
이를 사용한 예시는 다음과 같다.
List<Apple> redApples = filter(inventory, (Apple apple)
-> GREEN.equals(apple.getColor()));
List<Integer> eventNum = filter(numbers, (Integer i) -> i % 2 == 0);
동작 파라미터화는 즉, 동작을 캡슐화 한 다음 메소드로 전달해서! 메소드의 동작을 파라미터화 하는 것을 말한다.
public interface Comparator<T>{
int compare(T o1, T o2);
}
inventory.sort( (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
자바 쓰레드를 사용하면 병렬로 코드블록을 실행할 수 있다. Runnable 인터페이스를 이용해서 실행할 코드블록을 지정해보자.
Thread t = new Thread(new Runnable() {
public void run(){
System.out.println("Hello world!");
}
})
// lamdba
Thread t = new Thread( () -> System.out.println("Hello world!"));