ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [모던자바인액션] chap 02 동작 파라미터화 코드 전달하기
    JAVA 2021. 2. 27. 22:46
    반응형

    동작 파라미터화

    동작 파라미터화를 이용하면 자주 변하는 요구사항에 효과적으로 대응 가능.
    동작 파라미터화란 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미한다.
    이 코드 블록은 나중에 프로그램에서 호출한다.
    동작 파라미터화로 다양한 기능을 수행할 수 있다.

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

    기존의 농장 재고목록 애플리케이션에 리스트에서 녹색 사과만 필터링하는 기능을 추가한다고 가정하자.

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

    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;
      }
    
      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(Don't Repeat Yourself) 같은 것을 반복하지 말것 원칙을 어기는 것.


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

    public static List<Apple> filterApples(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;
      }

    형편없는 코드. true와 false는 뭘 의미하고 앞으로 요구사항이 바뀌었을 때 유연하게 대응할 수 없다.
    어떤 기준으로 사과를 필터링할 것인지 효과적으로 전달할 수 있다면 더 좋을 것이다.


    네 번째 시도: 추상적 조건으로 필터링

    참 또는 거짓을 반환하는 함수를 프레디케이트라고 한다. 선택 조건을 결정하는 인터페이스를 정의하자.

    public static List<Apple> filter(List<Apple> inventory, ApplePredicate p) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
          if (p.test(apple)) {
            result.add(apple);
          }
        }
        return result;
      }
    
    interface ApplePredicate {
    
        boolean test(Apple a);
    
      }
    
    static class AppleWeightPredicate implements ApplePredicate {
    
        @Override
        public boolean test(Apple apple) {
          return apple.getWeight() > 150;
        }
    
      }
    
      static class AppleColorPredicate implements ApplePredicate {
    
        @Override
        public boolean test(Apple apple) {
          return apple.getColor() == Color.GREEN;
        }
    
      }
    
    static class AppleRedAndHeavyPredicate implements ApplePredicate {
    
        @Override
        public boolean test(Apple apple) {
          return apple.getColor() == Color.RED && apple.getWeight() > 150;
        }
    
      }

    이렇게 동작 파라미터화, 즉 메서드가 다양한 동작 (또는 전략) 을 받아서 내부적으로 다양한 동작을 수행할 수 있음.

    컬렉션 탐색 로직과 각 항목에 적용할 동작을 분리할 수 있다는 것이 동작 파라미터화의 강점.


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

    위 방법을 사용하려면 인터페이스를 구현하는 여러 클래스를 정의한 다음에 인스턴스화해야 하기에 상당히 번거로워
    익명 클래스 기법을 사용.

    익명 클래스 - 자바의 지역 클래스(블록 내부에 선언된 클래스)와 비슷한 개념이다.
    즉, 즉석에서 필요한 구현을 만들어서 사용할 수 있음.

    List<Apple> redApples2 = filter(inventory, new ApplePredicate() {
          @Override
          public boolean test(Apple a) {
            return a.getColor() == Color.RED;
          }
        });

    다만 부족한 점이 존재.

    첫째, 익명 클래스는 여전히 많은 공간을 차지함.

    둘째, 많은 프로그래머가 익명 클래스의 사용에 익숙하지 않음.


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

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

    이 전 코드보다 훨씬 간결하고 깔끔해짐.


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

    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;
    }

    이제 바나나, 오렌지, 정수, 문자열 등의 리스트에 필터 메서드를 사용할 수 있다.

    동작 파라미터화 패턴은 동작을 (한 조각의 코드로) 캡슐화한 다음에 메서드로 전달해서 메서드의 동작을 파라미터화한다.

    728x90

    'JAVA' 카테고리의 다른 글

    [모던자바인액션] chap 04 스트림 소개  (0) 2021.02.27
    [모던자바인액션] chap 03 람다 표현식  (0) 2021.02.27
    [Study] 자바 9주차 과제  (0) 2021.01.27
    [Study] 자바 8주차 과제  (0) 2021.01.24
    [Study] 자바 7주차 과제  (0) 2021.01.18

    댓글

Designed by Tistory.