앞으로 모던 자바 인 액션 책을 읽고 기억해야 할 내용이나 새로 알게 된 내용을 정리해보려 한다.
아래는 2장 동작 파라미터화 코드 전달하기의 일부 내용이다.
우리가 어떤 상황에서 일을 하던 소비자의 요구사항은 항상 바뀐다.
시시각각 변하는 사용자 요구사항에 어떻게 대응해야 할까?
→ 동작 파라미터화를 이용하자!
동작 파라미터화란?
: 아직은 어떻게 실행할 것인지 결정하지 않은 코드블록을 의미한다.
첫번째 시도
: 기존의 농장 재고목록 애플리케이션에 리스트에서 녹색 사과만 필터링 하는 기능을 추가해 보자!
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;
}
이제 아래처럼 구현한 메소드를 호출 할 수 있다.
List<Apple> greenApples = filterApplesByColor(inventory, GREEN);
List<Apple> redApples = filterApplesByColor(inventory, RED);
만약 색이 아닌 무게로 필터링을 하려면 어떻게 할까?
색과 마찬가지로 filterApplesByWeight 메소드를 만들아서 사용 할 수 있지만 이런 좋은 해결책이 아니다!
중첩되는 코드들이 존재하기 때문이다.( 이는 소프트웨어의 DRY-Don’t Repeat Yourself 원칙을 어기는것!)
세번째 시도
: 동작 파라미터화를 이용해보자
우리는 사과의 어떤 속성에 기초해서 boolean 값을 반환하는 방법을 사용해보자!
( 사과가 녹색인가? 150그램 이상인가?)
→ 참 또는 거짓을 반환하는 함수를 프레디케이트(predicate) 라고 한다. 선택 조건을 결정하는 인터페이스를 정의하자!
public interface ApplePradicate {
boolean test(Apple apple);
}
// 녹색 사과만 선택
public class AppleGreenColorPredicate implements ApplePradicate {
public boolean test(Apple apple) {
return GREEN.equals(apple.getColor());
}
}
// 150그램 이상인 사과만 선택
public class AppleHeavyWeightPredicate implements ApplePradicate {
public boolean test(Apple apple) {
return apple.getWeight() > 150;
}
}
ApplePredicate는 사과 선택 전략을 캡슐화 했다.
( 조건에 따라 filter 메소드가 다르게 동작함 → 전략 디자인 패턴 이라고 함)
전략 디자인 패턴은 각 알고리즘( 전략 )을 캡슐화 하고 알고리즘 패밀리를 정의해 둔 다음 런타임에 알고리즘을 선택하는 기법이다.
ApplePredicate → 알고리즘 패밀리
AppleGreenColorPredicate, AppleHeavyWeightPredicate → 알고리즘
네번째시도
: 추상적 조건으로 필터링
ApplePredicate 객체를 받아 사과의 조건을 검사하도록 메소드를 수정해보자.
// 동작 파라미터화
public static List<Apple> filterApples(List<Apple> inventory, ApplePradicate ap){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (ap.test(apple)) { // 이렇게 하면 두가지 다 검사 가능 (무게와 색 ) -> 두가지 클래스에서 test를 구현 했기 때문
result.add(apple);
}
}
return result;
}
우리는 ApplePredicate 를 이용하여 소비자가 요구하는 다양한 사항들을 적절하게 구현하는 클래스만 만들어주면 된다.
ApplePredicate 객체를 filterApples 메서드에 전달하여 메서드의 동작을 결정 할 수 있다.
→ 즉, filterApples 메서드의 동작을 파라미터화 한것 ! (아래 예제 참고)
public class AppleRedHeavyWeightPredicate implements ApplePradicate {
@Override
public boolean test(Apple apple) {
return RED.equals(apple.getColor()) &&
apple.getWeight() > 150;
}
}
// 동작 파라미터한 필터(AppleRedHeavyWeightPredicate)를 전달한다.
List<Apple> redAndHeavyApples =
filterApples(inventory, new AppleRedHeavyWeightPredicate());
이처럼 동작 파라미터화는 한개의 파라미터로 여러가지의 동작을 할 수 있게 해준다.
(filterApples 메서드의 파라미터는 ApplePredicate 객체 하나지만 동작 파라미터화로 여러가지 동작이 가능하다.)
따라서 유연한 API를 만들때 동작 파라미터 화가 중요한 역할을 한다.
'JAVA' 카테고리의 다른 글
Model Mapper 사용법 (0) | 2023.01.26 |
---|---|
동작 파라미터화 코드 전달하기(2) (0) | 2023.01.19 |
int 와 long (0) | 2022.12.27 |
BDD 스타일 Mockito API (0) | 2022.12.26 |
Mock 객체 확인 (0) | 2022.12.15 |