-
[모던자바인액션] chap 02 동작 파라미터화 코드 전달하기JAVA 2021. 2. 27. 22:46반응형
동작 파라미터화
동작 파라미터화를 이용하면 자주 변하는 요구사항에 효과적으로 대응 가능.
동작 파라미터화란 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미한다.
이 코드 블록은 나중에 프로그램에서 호출한다.
동작 파라미터화로 다양한 기능을 수행할 수 있다.변화하는 요구사항에 대응하기
기존의 농장 재고목록 애플리케이션에 리스트에서 녹색 사과만 필터링하는 기능을 추가한다고 가정하자.
첫 번째 시도: 녹색 사과 필터링
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; }
빨간 사과도 필터링하고 싶어지면 메서드를 복사해서 filterRedApples라는 메서드 생성. 변화에 적절하게 대응할 수 없다.
두 번째 시도: 색을 파라미터화
public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) { List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (apple.getColor().equals(color)) { result.add(apple); } } return result; } public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) { List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (apple.getWeight() > weight) { result.add(apple); } } return result; }
요구사항이 추가되었을 때 무게 정보 파라미터도 추가함.
하지만 각 사과에 필터링 조건을 적용하는 부분의 코드가 색 필터링 코드와 대부분 중복됨.
소프트웨어 공학의 DRY(Don't Repeat Yourself) 같은 것을 반복하지 말것 원칙을 어기는 것.
세 번째 시도: 가능한 모든 속성으로 필터링
public static List<Apple> filterApples(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; }
형편없는 코드. true와 false는 뭘 의미하고 앞으로 요구사항이 바뀌었을 때 유연하게 대응할 수 없다.
어떤 기준으로 사과를 필터링할 것인지 효과적으로 전달할 수 있다면 더 좋을 것이다.
네 번째 시도: 추상적 조건으로 필터링
참 또는 거짓을 반환하는 함수를 프레디케이트라고 한다. 선택 조건을 결정하는 인터페이스를 정의하자.
public static List<Apple> filter(List<Apple> inventory, ApplePredicate p) { List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (p.test(apple)) { result.add(apple); } } return result; } interface ApplePredicate { boolean test(Apple a); } static class AppleWeightPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { return apple.getWeight() > 150; } } static class AppleColorPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { return apple.getColor() == Color.GREEN; } } static class AppleRedAndHeavyPredicate implements ApplePredicate { @Override public boolean test(Apple apple) { return apple.getColor() == Color.RED && apple.getWeight() > 150; } }
이렇게 동작 파라미터화, 즉 메서드가 다양한 동작 (또는 전략) 을 받아서 내부적으로 다양한 동작을 수행할 수 있음.
컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 있다는 것이 동작 파라미터화의 강점.
다섯 번째 시도: 익명 클래스 사용
위 방법을 사용하려면 인터페이스를 구현하는 여러 클래스를 정의한 다음에 인스턴스화해야 하기에 상당히 번거로워
익명 클래스 기법을 사용.익명 클래스 - 자바의 지역 클래스(블록 내부에 선언된 클래스)와 비슷한 개념이다.
즉, 즉석에서 필요한 구현을 만들어서 사용할 수 있음.List<Apple> redApples2 = filter(inventory, new ApplePredicate() { @Override public boolean test(Apple a) { return a.getColor() == Color.RED; } });
다만 부족한 점이 존재.
첫째, 익명 클래스는 여전히 많은 공간을 차지함.
둘째, 많은 프로그래머가 익명 클래스의 사용에 익숙하지 않음.
여섯 번째 시도: 람다 표현식 사용
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; }
이제 바나나, 오렌지, 정수, 문자열 등의 리스트에 필터 메서드를 사용할 수 있다.
동작 파라미터화 패턴은 동작을 (한 조각의 코드로) 캡슐화한 다음에 메서드로 전달해서 메서드의 동작을 파라미터화한다.
728x90'JAVA' 카테고리의 다른 글
[모던자바인액션] chap 04 스트림 소개 (0) 2021.02.27 [모던자바인액션] chap 03 람다 표현식 (0) 2021.02.27 [Study] 자바 9주차 과제 (0) 2021.01.27 [Study] 자바 8주차 과제 (0) 2021.01.24 [Study] 자바 7주차 과제 (0) 2021.01.18