0. 목적
변화하는 요구사항에 유연하게 대응할 수 있는 코드를 구현하자.
- 비용은 최소화
- 구현은 쉽게
- 유지보수가 쉽게
동작(Behavior) 파라미터화
메서드가 다양한 '동작'을 받아서 내부적으로 다양한 동작을 수행할 수 있다.
말 그대로 동작이 파라미터로 사용된다.
1. 변화하는 요구사항에 대응하기
🧑🌾 : 나는 녹색 사과만 보고싶어.
1.1. 녹색 사과 필터링
public enum Color {
RED,
GREEN
}
@Data
@AllArgsConstructor
public class Apple {
public Color color;
}
public class 녹색사과필터링 {
public static List<Apple> filterGreenApplesList(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (Color.GREEN.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
}
🧑🌾 : 빨간 사과도 볼 수 있는 방법은 없을까 ?
1.2. 색을 파라미터화
public class 색을파라미터화 {
public static List<Apple> filterGreenApplesList(List<Apple> inventory, Color color){
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
// 파라미터로 받은 color 로 비교
if (apple.getColor().equals(color)) {
result.add(apple);
}
}
return result;
}
}
🧑🌾 : 색 이외에도 가벼운 사과와 무거운 사과로 구분하고 싶어. 150그램 이상이면 무거운 사과야.
1.3. 속성 필터링
public class 가능한_모든_속성으로필터링 {
public static List<Apple> filterGreenApplesList(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;
}
}
가독성이 떨어지고, 필터링 조건이 다시 달라졌을 때 대응하기 힘들다.
2. 동작 파라미터화
🗣 그저 값 파라미터를 추가하는 것이 아닌, 좀 더 유연하게 대응할 수 있는 방법은 없을까 ?
public interface ApplePredicate {
boolean test(Apple apple);
}
public class 추상조건으로_필터링 {
public static List<Apple> filterApples(List inventory, ApplePredicate p){
List<Apple> result = new ArrayList<>();
for(Apple apple : inventory){
// 프레디케이트 객체로 사과 검사조건을 캡슐화했다.
if(p.test(apple)){
result.add(apple);
}
}
return result;
}
}
class AppleHeavyPredicate implements ApplePredicate{
public boolean test(Apple apple) {
//무게 비교
return false;
}
}
class AppleColorPredicate implements ApplePredicate{
public boolean test(Apple apple) {
//색상 비교
return false;
}
}
public class FilteringApples {
public static void main(String[] args) {
List<Apple> inventory = Arrays.aslist(new Apple(80, GREEN),
new Apple(155, GREEN),
new Apple(120, RED));
List<Apple> heavyApples = filterApples(inventory, new AppleHeavyWeightPredicate());
List<Apple> greenApples = filterApples(inventory, new AppleGreenColorPredicate ());
}
filterApples() 메서드의 동작을 파라미터화 했다 !
🗣 새로운 동작을 하기 위해서는 인터페이스를 모두 구현한 클래스를 정의하며, 인스턴스화 해야해. 번거롭고 시간낭비야 !
3. 익명 클래스
이름이 없는 클래스. 클래스 선언과 인스턴스화를 동시에 할 수 있다.
즉, 즉석에서 필요한 구현을 만들어 사용할 수 있다.
* 인스턴스화 ? 인스턴스라는 것이 어떤 클래스로부터 만들어진 객체를 의미하기 때문에 / 클래스로부터 객체화하는 것.
public class 익명클래스 {
public static List<Apple> filterApplesList(List<Apple> inventory,
ApplePredicate applePredicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (applePredicate.test(apple)) {
result.add(apple);
}
}
return result;
}
// 익명클래스 구현
public void run() {
List<Apple> inventory = List.of(
new Apple(Color.GREEN, 50),
new Apple(Color.RED, 111),
new Apple(Color.GREEN, 13)
);
List<Apple> apples = filterApplesList(inventory, new ApplePredicate() {
@Override
public boolean test(Apple apple) {
return Color.RED.equals(apple.getColor());
}
});
}
}
4. 람다 표현식
public class 람다표현식 {
public static List<Apple> filterApplesList(List<Apple> inventory,
ApplePredicate applePredicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : inventory) {
if (applePredicate.test(apple)) {
result.add(apple);
}
}
return result;
}
// 익명클래스 구현
public void run() {
List<Apple> inventory = List.of(
new Apple(Color.GREEN, 50),
new Apple(Color.RED, 111),
new Apple(Color.GREEN, 13)
);
//람다로 깔끔해짐
List<Apple> result = filterApplesList(inventory, (Apple apple) -> Color.RED.equals(apple.getColor()));
}
}
5. 리스트 형식으로 추상화
public class 리스트형식으로_추상화 {
public static <T> List<T> filterList(List<T> inventory,
Predicate<T> predicate) {
List<T> result = new ArrayList<>();
for (T e : inventory) {
if (predicate.test(e)) {
result.add(e);
}
}
return result;
}
public void run() {
List<Apple> inventory = List.of(
new Apple(Color.GREEN, 50),
new Apple(Color.RED, 111),
new Apple(Color.GREEN, 13)
);
List<Integer> numbers = List.of(1, 2, 3, 4);
// 제네릭 추상화
List<Apple> result = filterList(inventory, (Apple apple) -> Color.RED.equals(apple.getColor()));
List<Integer> numberList = filterList(numbers, (Integer i ) -> i%2 == 0);
}
}
6. 결론
- 동작 파라미터화에서는 메서드 내부적으로 다양한 동작을 수행할 수 있도록 코드를 메서드 인수로 전달한다.
- 변화하는 요구사항에 더 잘 대응할 수 있는 코드를 구현할 수 있다.
- 코드 전달기법을 사용하여 동작을 메서드의 인수로 전달할 수 있다.
'모던자바인액션' 카테고리의 다른 글
[Part6] 함수형 프로그래밍과 자바 진화의 미래 (A) (0) | 2023.10.24 |
---|---|
[Part1] Chapter3. 람다 (1) | 2023.09.04 |