시스템 요구사항은 끊임없이 변하고, 우리는 변화에 유연한 코드를 작성해야할 의무가 있다.
중복 코드를 줄이고, 변화에 유연한 코드를 작성하기 위해서는 어떻게 해야할까 ?
동작 파라미터화
첫 날 농부 클라이언트가 "녹색 사과를 모두 찾고 싶어요" 라는 요구사항을 보내왔다.
우리는 filterGreenApples 메서드에 GREEN.equals(apple.getColor()) 조건을 통해 녹색인 사과를 찾아 요구사항을 만족했다.
하지만 다음 날 농부 클라이언트는 "무게가 150g 이상인 사과를 모두 찾고 싶어요" 라는 요구사항 수정을 보내왔다.
우리는 다시 filterApplesByWeight 메서드를 작성해 요구사항을 만족했다.
바로 다음 날 농부 클라이언트는 "무게가 150g 이상인 빨간 사과를 모두 찾고 싶어요" 라는 요구사항 수정을 보내왔다.
우리는 기존의 작성했던 메서드를 또 다시 수정하고 반복해야한다.
즉 요구사항에 유연하게 대응하지 못 하고, 매번 코드를 수정해야하는 번거로운 작업에 시달리게된다.
우리는 동작 파라미터화를 이용해 자주 바뀌는 요구사항에 효과적으로 대응할 수 있다.
동작 파라미터화란 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블럭을 의미한다.
해당 코드 블록은 후에 런타임시 프로그램에서 호출한다. 즉 실행이 나중으로 미뤄진다는 뜻이다.
예를 들에 나중에 실행될 메서드의 인수로 코드 블록을 전달 할 수 있다.
앞선 예제를 동작파라미터화 해보자.
참 혹은 거짓을 반환하는 함수를 Predicate 라고 한다. 우선 선택 조건을 결정하는 인터페이스를 먼저 정의하자.
public interface ApplePredicate {
boolean test(Apple apple);
}
그 후 우리는 ApplePredicate 를 색으로 구분하거나 무게로 구분하는 구현체를 작성해, 런타임시 선택해 실행할 수 있다.
이를 전략 디자인 패턴이라 부른다. 메서드에는 ApplePredicate p 를 매개변수로 넘겨, p.test(apple) 메서드를 통해 넘겨받은 구현체 메서드를 실행할 수 있다. 덕분에 컬렉션을 반복하는 로직과 컬렉션 각 요소에 적용할 동작을 분리할 수 있다는 점에서 소프트웨어 엔지니어링적으로 큰 이득을 얻었다. 변경사항이 있을 시에는 해당 로직이 아닌, 호출하는 매개변수만 변경해주면 된다.
하지만 새로운 요구사항이 생길 때마다 ApplePredicate 의 구현체를 새롭게 생성해야한다는 단점이 아직 존재한다.
람다 표현식
자바8 이후에서는 람다 표현식을 활용해 더 간단하게 코드를 파라미터로 전달할 수 있다.
자세한 람다 표현식은 다음 포스팅에서 이어가도록 하고, 예제 코드를 소개하며 포스팅을 마치겠다.
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> redApples = filter(inventory, (Apple apple) -> RED.equals(apple.getColor()));
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i%2 == 0);
'개인 공부 > Java' 카테고리의 다른 글
[오브젝트] 클래스를 분리해야할 때 (0) | 2023.08.08 |
---|---|
[오브젝트] 객체지향설계 (0) | 2023.08.08 |
[Java] 제네릭 (0) | 2022.09.15 |