시작하기 앞서

드디어 리팩터링, 테스팅, 디버깅 파트다. 우테코 프리코스를 진행하면서 가장 답답했던 부분을 해소할 수 있을까? 프리코스를 진행하는 동안 계속 이렇게 코딩을 하는게 맞나? 하는 생각이 들었다. 이제 책들을 읽으며 잘못된 코드들을 뜯어 고쳐봐야겠다. 이 책에서는 람다 표현식으로 strategy, template method, observer, chain of responsibility, factory 등의 객체 지향 디자인 패턴을 간소화하는 방법도 살펴본다고 한다. 더불어 람다와 스트림 코드를 테스트하고 디버깅 하는 방법도 알려준다고 한다. 기대가 된다. 시작해보자!

코드 가족성 개선

람다, 메소드 참조, 스트림을 활용해서 코드 가독성을 개선할 수 있는 간단한 세가지 리팩터링 방법이 있다.

  • 익명 클래스를 람다로 리팩토링
  • 람다를 메소드 참조로 리팩토링
  • 명령형 데이터 처리를 스트림으로 리팩토링

익명 클래스를 람다로 리팩토링 주의사항

  • 모든 익명 클래스를 람다로 변환할 수 있는 것은 아니다. 익명 클래스에서 사용한 this와 super는 람다에서는 다른 의미를 가진다.

    • < 익명 클래스에서 this - 익명클래스 자신을 가리킨다. / 람다에서 this - 람다를 감싸는 클래스를 가리킨다. >
  • 익명 클래스는 감싸고 있는 클래스의 변수를 가릴 수 있다. 람다에서는 안된다.

    int a = 10;
    Runnable r1 = () => {
      int a = 2;            // 컴파일 에러
      System.out.println(a);
    };
    Runnable r2 = new Runnable() {
      public void run() {
        int a = 2;            // 잘 작동함
        System.out.println(a);
      }
    };
  • 익명 클래스를 람다 표현식으로 바꾸면 콘텍스트 오버로딩에 따른 모호함이 초래된다.

    • 익명 클래스는 인스턴스화할 때 명시적으로 형식이 정해진다.
    • 람다의 형식은 콘텍스트에 따라 달라진다.

람다 표현식을 메서드 참조로 리팩터링

  • 람다 표현식이 길어지거나 의도가 모호하다면 메서드화 시킨다. 메서드화 시키면 메서드명으로 코드의 의도를 명확하게 알릴 수 있다.
  • comparing, maxBy 같은 정적 헬퍼 메서드를 적극적으로 활용하자. 메서드 참조와 조화를 이루도록 설계되었다.
    inventory.sort(
      (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())); // 같은 동작의 코드
    inventory.sort(comparing(Apple::getWeight));                           // 같은 동작의 코드
  • sum, maximum 등 자주 사용하는 리듀싱 연산을 Collectors API를 사용하자
    int totalCalories = 
      menu.stream().map(Dish::getCalories)
                   .reduce(0, (c1, c2) -> c1 + c2);
    int totalCalrories = menu.stream().collect(summingInt(Dish::getCalories));

명령형 데이터 처리를 스트림으로 리팩터링

이론적으로는 반복자를 이용한 기존의 모든 컬렉션 처리 코드를 스트림 API로 바꿔야한다. 스트림 API는 데이터 처리 파이프라인의 의도를 더 명확하게 보여주기 때문이다. 쇼트서킷과 게으름이라는 최적화와 멀티코어를 잘 활용할 수 있기 때문이다.

코드 유연성 개선(동작 파라미터화 behavior parameterization)

  • 함수형 인터페이스
    • 조건부 연기 실행(conditional deferred execution)
      if (logger.isLoggable(Log.FINER)) {
        logger.finer("Problem: " + generateDiagnostic());
      }
      이 코드는 메시지를 로깅할 때마다 logger 객체의 상태를 매번 확인하며 코드를 어지럽힌다. if문을 제거해보자.
      logger.log(Level.FINER, "Problem: " + generateDiagnostic());
      위와 같이 내부적으로 적절하게 설정되어있는지 확인하는 log 메서드를 구현하여 사용하자.
    • 실행 어라운드(excution around)
      같은 동작을 하는 준비, 종료 과정을 반복한다면 람다로 변환할 수 있다.
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기