월루를 꿈꾸는 대학생

[Java] 동작 파라미터화 본문

Programing/Java

[Java] 동작 파라미터화

하즈시 2023. 8. 5. 01:15
728x90

소비자의 요구사항은 항상 바뀐다 ➡️ 기능 추가는 쉽고 유지보수가 간편한 코드가 필요

동작 파라미터화

  • 어떻게 실행할 것인지 결정하지 않은 코드 블록
  • 나중에 프로그램에서 호출 즉 실행이 뒤로 미뤄짐

2.1 변화하는 요구사항에 대응하기

  • 예제 : ==기존의 농장 재고목록에서 녹색 사과만 필터링 하는 기능 추가==

2.1.1 첫번째 시도 : 녹색 사과 필터링

public static List<Apple> filterGreenApples(List<Apple> inventory) {  
  List<Apple> result = new ArrayList<>();  
  for (Apple apple : inventory) {  
    if (apple.getColor() == Color.GREEN) {  // 녹색 사과만 선택
      result.add(apple);  
    }  
  }  
  return result;  
}

녹색 사과만 필터링 하는 코드를 작성했는데 만약 추후에 녹색이 아니라 빨간색 사과를 필터링하고 싶으면? 혹은 다른 색상들을 구별하고 싶으면 어떻게 할까?

비슷한 코드가 반복 존재한다면 그 코드를 추상화하자!!

2.1.2 두번째 시도 : 색을 파라미터화

public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {  
  List<Apple> result = new ArrayList<>();  
  for (Apple apple : inventory) {  
    if (apple.getColor() == color) {  
      result.add(apple);  
    }  
  }  
  return result;  
}

// 호출할 함수들 
List<Apple> greenApples = filterApplesByColor(inventroy, Green);
List<Apple> redApples = filterApplesByColor(inventroy, Red);

필터링 하고자 하는 색을 인수로 넣어서 필터링 ➡️ 요구사항에 대응 완료

다만 색 이외에 농부가 무게도 추가적으로 알고 싶다고 한다면 ??

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 (같은 것을 반복하지 말것) 원칙 위반

2.1.3 세번째 시도 : 가능한 모든 속성으로 필터링

public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight, Color color, boolean flag) {  
  List<Apple> result = new ArrayList<>();  
  for (Apple apple : inventory) {  
    if ((flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight)) {  
    // flag로 색인지 무게인지 필터링할 거 정하고 필터링한다
      result.add(apple);  
    }  
  }  
  return result;  
}

형편없는 코드
true와 false가 뭘 의미하는지 파악하기 힘들고 유연한 대응도 불가

2.2 동작 파라미터화

사과의 어떤 속성에 기초해서 참/거짓을 반환하는 함수 프리디케이트
선택 조건을 결정하는 인터페이스

public interface ApplePredicate{
    boolean test (Apple apple);
}

다양한 선택 조건에 따라 여러 버전의 ApplePredicate정의 가능

public class AppleHeavyWeightPredicate implements ApplePredicate{
    public boolean test(Apple apple){
        return apple.getWeight()>150;
    }
}

public class AppleGreenColorPredicate implements ApplePredicate{
    public boolean test(Apple apple){
        return GREEN.equals(apple.getColor());
    }
}

![[Pasted image 20230710002357.png]]

전략 디자인패턴

  • 조건에 따라 filter메서드가 다르게 동작
  • 즉 전략에 따라 다르게 동작
  • 각 알고리즘 (전략)을 캡슐화하는 알고리즘 패밀리를 정의하고 런타임에 알고리즘을 선택하는 기법

filterApples에서 ApplePredicate객체를 받아서 애플의 조건을 검사하도록 메서드르 고쳐야한다
동작 파라미터화 : 메서드가 다양한 동작 (전략=함수 알고리즘)을 받아서 내부적으로 다양한 동작을 수행가능

2.2.1 네번째 시도

public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p){
    List<Apple> result = new ArrayList<>();
    for(Apple apple: inventory){
        if(p.test(apple)){ // predicate로 사과 검사 조건을 캡슐화 
            result.add(apple);
        }
    }
    returnm result;
}

더욱 유연해진 코드
AppliePredicate를 상속하는 다양한 요구사항에 맞는 전략들을 구성하면 보다 편리하게 사용 가능

static class AppleRedAndHeavyPredicate implements ApplePredicate {  

  @Override  
  public boolean test(Apple apple) {  
    return apple.getColor() == Color.RED && apple.getWeight() > 150;  
  }  

}

![[Pasted image 20230710003152.png]]

한 개의 파라미터 , 다양한 동작

동작파리미터 : 컬렉션 탐색 로직과 각 항목에 동작 분리가 강점
한 메서드가 다른 동작 수행할 수 있도록 재활용 가능

![[Pasted image 20230710003441.png]]

2.3 복잡한 과정 간소화

public class FilteringApples {  
  public static void main(String... args) {  
    List<Apple> inventory = Arrays.asList(  
        new Apple(80, Color.GREEN),  
        new Apple(155, Color.GREEN),  
        new Apple(120, Color.RED));  

    List<Apple> greenApples = filterApplesByColor(inventory, new AppleGreenColorPredicate());  

    List<Apple> heavyApples = filter(inventory, new AppleHeavyWeightPredicate());  

    }

    public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p ){
    List<Apple> result = new ArrayList<>();
    for(Apple apple: inventory){
        if(p.test(apple)){ // predicate로 사과 검사 조건을 캡슐화 
            result.add(apple);
        }
    }
    returnm result;
    }

}

2.3.1 익명 클래스

익명 클래스

  • 지역 클래스와 비슷
  • 이름이 없는 클래스
  • 선언과 동시에 인스턴스화 가능 : 즉석에서 필요한 구현 만들기 가능

2.3.2 다섯번째 시도 : 익명 클래스 사용

List<Apple> redApples = filterApples(inventory, new ApplePredicate(){
    return RED.equlas(apple.getColor());
})

익명 클래스는 여전히 많은 공간을 차지한다

2.3.3 여섯번째 시도 : 람다 표현식 사용

람다 사용한 코드

List<Apple> result = filterApples(inventory, (Apple apple)-> RED.equals(apple.getColor()));

![[Pasted image 20230710004600.png]]

2.3.4 일곱번째 시도 : 리스트 형식으로 추상화


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)){
            reuslt.add(e)
        }
    }
    reutnr result;
}
List<Apple> redApples = filter(inventroy, (Apple apple) -> RED.equals(apple.getColor()));

List<Intger> evenNumbers = filter(numbers, (Integer i )-> i%2 ==0)''

동작 파라미터화 패턴은 동작을 한조각의 코드로 캡슐화 한 다음에 메서드로 전달해서 머서드의 동작을 파라미터화한다 (사과의 다양한 프리디케이트)

728x90