프로그래밍을 함에 있어서 유연한 설계를 하는 것은 매우 중요하다. 팀 프로젝트를 하면서 요구사항이 날마다 변하는 것을 느낀 후로 쉽게 변동시킬 수 있는 코드를 작성하는 것이 필요함을 느꼈다. 이번 모던 자바 인 액션 1부 2.2 동적 파라미터화를 읽으면서 이 부분에 대한 아이디어를 얻을 수 있었다.
동작 파라미터화란, 코드에서 실행될 수 있는 동작을 해당 코드가 실행되는 시점에서 결정할 수 있도록 하는 것이다. 메서드를 작성함에 있어서 특정 값, 특정 조건을 집어넣을 경우, 요구사항이 추가되거나 변경될 경우, 새로운 메서드를 만들거나 기존의 구현부를 수정해야 한다. 하지만 메서드에 실행될 동작을 파라미터로 넣는다면, 입력받은 동작에 맞는 로직을 수행할 수 있다. 이는 변동되는 요구사항에 알맞는 방법이다.
1. 유연하지 못한 코드
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라는 메서드를 추가하고, 위 코드의 if 문을 수정해야할 것이다. 벌써 두 개의 메서드만으로도 중복되는 부분이 상당하다. 따라서 사과의 색깔을 결정하는 것을 파라미터로 뽑으면 중복을 방지할 수 있을 것이다.
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;
}
이렇게 하면, 입력받은 색깔이 맞게 필터링하는 코드를 만들 수 있다. 하지만 사과 색깔 뿐만 아니라 무게까지 고려해야 한다면? 색깔과 무게 중 한 가지 기준을 선택하고 싶어서 flag라는 boolean 값을 추가할 수 있다.
List<Apple> greenApples = filterApples(inventory, GREEN, 0, true);
책에서 강조하는 절대로 사용하지 말아야 할 코드이다. 위 코드를 봤을 때, 해당 메서드가 무슨 역할을 하는지 모를뿐더러 저 true가 무엇을 의미하는지도 모른다. 이처럼 메서드 행동에 필요한 모든 속성들을 파라미터로 추가할 경우, 코드의 가독성이 떨어지게 된다.
2. 유연한 코드 (동작을 파라미터로 전달)
앞선 코드에서 살펴본 문제점은 코드가 중복되고, 이를 해결하기 위해 파라미터로 뽑을 경우, 가독성이 매우 떨어지게 된다는 것이었다. 이를 해결하기 위한 것이 함수형 인터페이스인 Predicate (특정 입력 값에 대한 Boolean 값을 반환)을 사용하는 것이다. 즉, 선택 조건을 결정하는 인터페이스를 정의하는 것이다.
public interface ApplePredicate {
boolean test (Apple apple);
}
public class AppleRedAndHeavyPredicate implements ApplePredicate {
public boolean test(Apple apple) {
return "red".equals(apple.getColor()) && apple.getWeight() > 150;
}
}
public static List<Apple> filterApplesByColor(List<Apple> inventory, ApplePredicate p) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
이처럼 특정 동작을 파라미터로 받을 경우, 메서드를 사용하는 쪽에서 동작을 결정할 수 있다.
// 커스텀한 함수형 인터페이스 구현 클래스를 인스턴스화 하여 전달
prettyPrintApple(inventory, new AppleFancyFormatter());
public static void prettyPrintApple(List<Apple> inventory, AppleFormatter formatter) {
for (Apple apple : inventory) {
String output = formatter.accept(apple);
System.out.println(output);
}
}
public interface AppleFormatter {
String accept(Apple apple);
}
public class AppleFancyFormatter implements AppleFormatter{
@Override
public String accept(Apple apple) {
String characteristic = apple.getWeight() > 150 ? "heavy" : "light";
return "A " + characteristic + " " + apple.getColor() + " apple";
}
}
이처럼 특정 인터페이스를 정의하고, 해당 인터페이스를 구현한 클래스를 통해 어떠한 동작을 수행할건지 지정할 수 있다. 그리고 실제 구현 메서드의 동작은 인터페이스의 메서드를 통해 결정될 수 있도록 지정하는 것이다.
하지만 이처럼 인터페이스를 구현한 클래스를 매번 정의하고, 인스턴스화 하는 것은 번거롭고 시간낭비이다.
그래서 생겨난 것이 익명 클래스이며, 이것을 간소화한 것이 람다식이다.
// 익명 클래스 : 클래스의 선언과 인스턴스화를 동시에 수행
prettyPrintApple(inventory, new AppleFormatter() {
@Override
public String accept(Apple apple) {
String characteristic = apple.getWeight() > 150 ? "heavy" : "light";
return "A " + characteristic + " " + apple.getColor() + " apple";
}
});
// 람다식
prettyPrintApple(inventory, apple -> {
String characteristic = apple.getWeight() > 150 ? "heavy" : "light";
return "A " + characteristic + " " + apple.getColor() + " apple";
});
public static void prettyPrintApple(List<Apple> inventory, AppleFormatter formatter) {
for (Apple apple : inventory) {
String output = formatter.accept(apple);
System.out.println(output);
}
}
public interface AppleFormatter {
String accept(Apple apple);
}
이처럼 새로 클래스를 정의하지 않고도 얼마든지 동작 파라미터화가 가능하다.
3. 얻어간 내용
핵심은 컬렉션의 탐색 로직과 각 요소에 적용할 동작을 분리시킬 수 있다는 것이다. 컬렉션을 사용한 메서드를 설계함에 있어서 공통되는 부분과 변동되는 부분을 파악하고 동작 파라미터를 적용한다면 유연한 설계를 할 수 있을 것이다. 우테코 프리코스를 진행하면서 유연한 설계에 고민이 많았는데, 이번에 얻게 된 아이디어를 잘 사용하여 리팩토링해보고자 한다.
또한 실제 자바 API의 코드들에 있는 여러 함수형 인터페이스들을 이해할 수 있는 인사이트를 얻었다. 이들이 왜 필요한 것이며, 이들이 무엇을 의미하는지, 어떠한 동작을 파라미터화한 것인지를 이해할 수 있게 되었다.
출처 :
https://www.yes24.com/Product/Goods/77125987
모던 자바 인 액션 - 예스24
자바 1.0이 나온 이후 18년을 통틀어 가장 큰 변화가 자바 8 이후 이어지고 있다. 자바 8 이후 모던 자바를 이용하면 기존의 자바 코드 모두 그대로 쓸 수 있으며, 새로운 기능과 문법, 디자인 패턴
www.yes24.com
'Java > 모던 자바 인 액션' 카테고리의 다른 글
[모던 자바 인 액션] Optional 클래스 (0) | 2023.11.24 |
---|