
장기적인 관점에서 유지보수가 쉬워야 함
동작 파라미터화를 이용하면 자주 바뀌는 요구사항에 효과적으로 대응할 수 있음
동작 파라미터화
변화에 대응하는 코드를 구현하는 것은 어려운 일
모범 사례
기존의 농장 재고 목록 애플리케이션에 리스트에서 녹색 사과만 필터링 하는 기능 추가 한다고 가정
enum Color{RED,GREEN}
public static List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple result = new ArrayList<>();
for ( Apple apple: inventory){
if(GREEN.equals(apple.getColor()){ // 녹색 사과만 필터링
result.add(apple);
}
}
return result;
}
이렇게 녹색 사과만 필터링을 할 수 있음
근데 갑자기 빨간색 사과를 필터링 하고 싶게 되면 메서드를 복사해서 새로운 메서드를 만들고 if 문의 조건을 빨간사과르 바꾸는 방법을 선택 할 수 있음
하지만 더 다양한 색을 필터링 할때 적절하게 대응 할 수 없음
→ 이렇게 반복될때는 코드를 추상화하는 좋은 규칙이 있음
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color)
{
List<Apple> result = new ArrayList<>();
for ( Apple apple: inventory){
if ( apple.getColor().equal(color)){
result.add(apple);
}
}
return result;
}
이렇게 하면
List greendApples = filterApplesByColor(inventory , GREEN);
이런식으로 호출 할 수 있음
하지만 이때 무게로도 판단하고 싶다고 하면 또 다시 메서드를 복사하고 조건만 수정해 중복된 메서드가 생길 것
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color, int weight, boolean flag))
{
List<Apple> result = new ArrayList<>();
for ( Apple apple: inventory){
if ((flag && appel.getColor().equals(color)) ||
(!gloag && apple.getWeigth() > weight){
result.add(apple);
}
}
return result;
}
이렇게 되면 형편 없는 코드가 됨
이걸 해결하기 위해 동작 파라미터화를 이용
참 거짓을 반환하는 함수를 프레디케이트
선택 조건을 결정하는 인터페이스 정의
public interface ApplePredicate{
boolean test (Apple apple);
}
public class AppleHeavyWeightPredicate implements ApplePredicate{
public boolean test(Apple apple){
return apple.getWegith()>150;
}
}
조건에 따라 filter 메서드가 다르게 동작할 것이라고 예상
→ 이를 전략 디자인 패턴
전략 디자인 패턴
ApplePredicate는 어떻게 다양한 동작 수행?
filterApples메서드가 ApplePredicate 객체를 인수로 받도록 수정
public static List<Apple> filterApples(List<Apple> inventory,ApplePrdicate p)
{
List<Apple> result = new ArrayList<>();
for ( Apple apple: inventory){
if ( p.test(apple){ //프레디케이트 객체로 사과 검사 조건을 캡슐화
result.add(apple);
}
}
return result;
}
이제 필요한 대로 다양한 ApplePredicate를 만들어서 filterApples 메서드로 전달 가능
ex ) 150 그램이 넘는 빨간 사과를 검색해달라고 부탁
public class AppleRedAndHeavyPredicate implements ApplePredicate{
public boolean test(Apple apple){
return RED.equlas(apple.getColor())
&& apple.getWegith()> 150;
}
}
한개의 파라미터, 다양한 동작
컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 잇다는 것이 동작 파라미터화 의 강점
위에처럼 한다면 로직과 관련없는 코드가 많이 추가 됨
자바에서는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스 기법 제공
익명 클래스
List<Apple> redApples = filterApples(inventory, new ApplePredicate(){
public boolean test(Apple apple){
return RED.equals(apple.getColor());
}
});
부족한점
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;
}
이제 바나나, 오렌지 , 정수, 문자열등의 리스트에 필터 메서드 사용 가능
컬렉션 정렬은 반복되는 프로그래밍 작업
자바 8 List 에는 sort 기능 포함
java.util.Comparator 객체를 이용해서 sort의 동작 파라미터화
//java.util.Comparator
public interface Comparator<t>{
int c ompar(T o1, To2);
}
sort 메서드의 동작 다양화
ex) 익명 클래스를 이용해서 무게가 적은 순서로 목록에서 사과를 정렬
inventory.sort(new Comparator<Apple>(){
public int compare(Apple a1, Apple a2){
return a1.getWright().commjpareTo(a2.getWeight());
}
});
//람다 사용하면
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
자바 스레드를 이용하면 병렬로 코드 블록을 실행할 수 있음
자바 8까지는 Thread 생성자에 객체만을 전달 할 수 있었으므로 보통 결과를 반환하지 않는 void run 메소드를 포함하는 익명 클래스가 Runnable 인터페이스를 구현 하도록 하는 것이 일반적
public interface Runnable{
void run();
}
Thread t = new Thread(new Runnable(){
public void run()}{
System.out.println("Hello world");
}
});
//람다
Thread t= new Thread(() -> Sysmtem.out.println("Hello World"));
ExecutorService 인터페이스
변화에 대응할 수 있는 유연한 코드 필요
모든 동작에 반응 할 수 있기 때문