사용자의 요구사항은 항상 변한다.
따라서 새로 추가하는 기능은 쉽게 구현할 수 있고 유지보수가 쉬워야한다.
동작 파라미터화 behavior parameterization
: 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록. 코드블록의 실행은 나중으로 미뤄진다.
-> 나중에 실행될 메서드의 인수로 코드블록을 전달할 수 있다.
->코드블록에 따라 메서드의 동작이 파라미터화 된다.
- 리스트의 모든 요소에 대해서 ''어떤 동작을 수행할 수 있음.
- 리스트 관련 작업을 끝낸 다음에 '어떤 다른 동작'을 수행할 수 있음.
- 에러가 발생하면 '정해진 어떤 다른 동작'을 수행할 수 있음.
첫번째 시도 : 녹색사과 필터링
enum Color { RED, GREEN }
public static List<Apple> filterGreenApple(List<Apple> inventory) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if(Green.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
거의 비슷한 코드가 반복 존재한다면 그 코드를 추상화한다
두번째 시도 : 색을 파라미터화
메서드에 색을 파라미터화 해서 다양한 색을 필터링 할 수 있다.
public static List<Apple> filterAppleByColor(List<Apple> inventory, Color color) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
if(apple.getColor().equals(color)) {
result.add(apple);
}
}
return result;
}
List<Apple> greenApples = filterAppleByColor(inventory, GREEN);
List<Apple> redApples = filterApplesByColor(inventory, RED);
색 이외의 무게가 150그램 이상인 사과도 구분하게 하려면 무게정보를 파라미터화해서 구현도 가능하다.
하지만 색 필터링 코드와 중복된 코드를 구현해야만하고 DRY dont repeat yourself 원칙을 어기는 것이다.
-> 탐색과정을 고치려면 메서드 전체 구현을 고쳐야 하므로 엔지니어링적 비용이 증가한다.
세번째 시도: 가능한 모든 속성으로 필터링
플래그 속성으로 기능 구분 형편없는 코드
public static List<Apple> filterAppleByColor(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;
}
List<Apple> greenApples = filterAppleByColor(inventory, GREEN, 0, true);
List<Apple> heavyApples = filterApplesByColor(inventory, null, 150, false);
요구사항이 바뀌었을때 유연하게 대응할수도 없는 코드다.
네번째 시도: 추상적 조건으로 필터링
사과 선택조건을 캡슐화 함.
전략 디자인 패턴 strategy design pattern ( http://en.wikipedia.org/wiki/Strategy_pattern 참고 )
-> 런타임에 알고리즘을 선택하는 패턴
public interface ApplePredicate {
boolean test (Apple apple);
}
public class AppleHeavyWeightPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
public class AppleGreenColorPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return GREEN.equals(apple.getColor());
}
}
public static List<Apple> filterAppleByColor(List<Apple> inventory,
ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory) {
//사과 검사조건을 캡슐화
if(p.test(apple)) {
result.add(apple);
}
}
return result;
}
//요구사항이 추가되면 새로운 클래스 추가
public class AppleRedAndHeavyPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return RED.equals(apple.getColor()) && apple.getWeight()>150 ;
}
}
// 전달한 ApplePredicate 객체에 의해 filterApples 메서드 동작이 결정
List<Apple> reaAndHeavyApples = filterApples(inventory, new AppleRedAndHeavyPredicate());
메서드의 동작을 파라미터화한 것.
유연한 API를 만들때 동작 파라미터화가 중요한 역할을 한다.
다섯번째 시도 : 익명클래스 사용
인터페이스를 구현하는 여러 클래스를 정의한 다음 인스턴드화 해야하는 작업또한 번거롭다.
클래스 선언과 인스턴스화를 동시에 수행할 수 있는 익명클래스를 사용하면 코드를 줄일 수 있다.
: 익명클래스, 지역 클래스 local class, 블록내부에 선언된 클래스
List<Apple> redApples = filterApples(inventory, new ApplePredicate() {
public boolean test(Apple apple) {
return RED.equals(apple.getColor());
}
});
코드의 장황함 verbosity 은 나쁜특성이다.
결국 객체를 만들고 새로운 동작을 정의하는 메서드를 구현해야 한다는 점은 변하지 않는다.
-> 람다 표현식을 사용해 정리할 수 있다.
여섯번째 시도 : 람다 표현식 사용
List<Apple> result = filterApples(inventory, (Apple apple)->RED.equals(apple.getColor()));
일곱번째 시도 : 리스트 형식으로 추상화
타입, 정수, 문자열 리스트에 필터메서드 사용 아름다운 코드다
public interface Predicate<T> {
boolean test(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.test(e)) {
result.add(e);
}
}
return result;
}
List<Apple> resApples = filter(inventory, (Apple apple)->RED.equals(apple.getColor()));
List<Integer> evenNumbers = filter(number, (Integer i)->i%2 == 0);
유연하고 간결하다.
변화하는 요구사항에 쉽게 대응할 수 있는 다양한 정렬동작을 수행할 수 있는 코드가 필요하다.
무게를 기준으로 목록에서 정렬하고 싶다면?
Comparator 로 정렬하기
자바 8 List 에 sort 메서드가 있다. java.util.Comparator 객체를 이용해 sort 동작을 파라미터화 할수 있다.
//java.util.Comparator
public interface Comparator<T> {
int compare(T o1, T o2);
}
//sort의 동작을 다양화
//익명클래스 이용할 경우
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.Weight());
}
});
//람다를 이용할 경우
inventory.sort (
(Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight()));
Runnable 로 코드 블록 실행하기
자바 스레드를 이용하면 병렬로 코드블록을 실행할 수 있다.
보통 익명클래스가 Runnable 인터페이스를 구현하도록 하는 것이 일반적이었으나
람다 표현식으로 스레드 코드를 구현할 수 있다.
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello world");
}
});
//람다 표현식
Thread t = new Thread(() -> System.out.println("Hello world"));
Callable 테스크 처리하기
ExecutorService 를 이용하면 태스크를 스레드 풀로 보내고 결과를 Future로 저장할 수 있다.
Callable 인터페이스를 이용해 결과를 반환하는 태스크를 만든다.
public interface Callable<V> {
V call();
}
ExecutorService excutorService = Executor.newCachedThreadPool();
Future<String> threadName = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return Thread.currentThread().getName();
}
});
//람다를 이용하면
Future<String> threadName = executorService.submit(
()->Thread.currentThread().getName());
GUI 이벤트 처리하기
자바FX 에서는 setOnAction 메서드에 EventHandler를 전달해 이벤트에 어떻게 반응할지 설정할 수 있다.
Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event){
lable.setText("Sent!!");
}
});
// 람다 표현식으로 구현
button.setOnAction((ActionEvent event)->label.setText("Sent!!"));
'IT Book Summary > ModernJavaInAction' 카테고리의 다른 글
Chapter5 스트림 활용 (0) | 2020.03.11 |
---|---|
Part 2 함수형 데이터 처리 - Chapter 4 스트림 소개 (3) | 2020.03.02 |
Chapter3 람다 표현식_02 (1) | 2020.03.01 |
Chapter3 람다 표현식 _ 01 (0) | 2020.02.27 |
Part 1 기초 - Chapter 1 자바8, 9, 10, 11 (0) | 2020.02.23 |