열렬히.뛰기

모던 자바 9. 리팩터링·테스팅·디버깅

Language > 프로그래밍 언어 > Java > 모던 자바 > 모던 자바 9. 리팩터링·테스팅·디버깅

1. 가독성 & 유연성을 개선하는 리팩터링

  • 익명 클래스 -> 람다 표현식
  • 람다 표현식 -> 메서드 참조
  • 명령형 데이터 처리 -> 스트림

익명 클래스를 람다 표현식으로 리팩터링

  • Before (명령형)
Runnable r1 = new Runnable() {
    public void run() {
        System.out.println("");
    }
}
  • After (함수형)
Runnable r = () -> System.out.println("Hello");

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

  • Before
inventory.sort((a, b) -> a.getWeight().compareTo(b.getWeight()));
  • After
inventory.sort(comparing(Apple::getWeight));
  • 장점
    • 의도(Intent)가 더 명확
    • 테스트·재사용 용이
    • 이름이 곧 문서 역할

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

  • Before
List<String> dishNames = new ArrayList<>();
for (Dish dish : menu) {
    if (dish.getCalories() > 300) {
        dishNames.add(dish.getName());
    }
}
  • After
List<String> dishNames =
    menu.stream()
        .filter(d -> d.getCalories() > 300)
        .map(Dish::getName)
        .toList();

2. 람다로 객체지향 디자인 패턴 리팩터링하기

람다는 전통적인 객체지향 디자인 패턴을 더 간결하고 명확하게 표현하게 해줌
이 장에서는 5가지 패턴을 어떻게 람다로 재구성할지를 보여준다.

전략 패턴

전략 패턴이란?
알고리즘(전략)을 캡슐화하여, 실행 중에 전략을 교체할 수 있도록 하는 패턴

  • 언제 쓰이나?
  • 검증 로직
  • 정렬 기준
  • 할인 정책
  • 조건별 처리 로직
  • 예시코드
  • 람다식을 사용하면 굳이 IsNumeric, IsAllLowerCase 등을 만들 필요가 없어짐.

템플릿 메서드 패턴

템플릿 메서드 패턴이란? 알고리즘의 골격은 상위 클래스에 두고, 일부 단계만 하위 클래스에서 구현하도록 하는 패턴
언제 쓰이나? 공통 처리 흐름이 있고 일부 단계만 다를 때 프레임워크 설계

  • Before
  • After
  • 람다식을 사용하면 OnlineBanking 클래스를 상속받지 않고 직접 람다 표현식을 전달해 다양한 동작을 추가.

옵저버 패턴

옵저버 패턴이란? 객체의 상태 변화가 발생했을 때, 등록된 다른 객체들에게 자동으로 알리는 패턴
언제 쓰이나?
• 이벤트 시스템
• 알림
• UI 갱신
• 메시지 브로드캐스트

  • 예시코드 : 알람 시스템 설계 (특정 키워드를 포함하는 트윗이 등록되면 알람 전송)
  • 람다식을 사용하면 클래스를 직접 만들지 않고도 다양한 동작을 추가하는 것이 가능

책임 연쇄 패턴

책임연쇄 패턴이란? 요청을 처리할 수 있는 객체들을 체인으로 연결하여, 순서대로 요청을 전달하는 패턴
언제 쓰이나?
• 파이프라인 처리
• 필터
• 요청 전처리
• 문자열 가공

  • 예시코드 : 알람 시스템 설계 (특정 키워드를 포함하는 트윗이 등록되면 알람 전송)
  • 람다식을 사용하면 클래스를 직접 만들지 않고도 다양한 동작을 추가하는 것이 가능

팩토리 패턴

팩토리 패턴이란? 객체 생성 로직을 캡슐화하여, 생성 방식 변경에 유연하게 대응하는 패턴
언제 쓰이나?
• 조건에 따라 다른 객체 생성
• 객체 생성 책임 분리

  • 예시코드 : 은행 상품을 만드는 예시
  • 람다식을 사용하면 클래스를 직접 만들지 않고도 다양한 동작을 추가하는 것이 가능

3. 람다 테스팅

람다와 스트림을 사용한 코드를 어떻게 테스트해야 하는지에 대한 기본 원칙을 이해한다.

  • 테스트하기 쉬운 람다·스트림 코드의 특징

    • 입력과 출력이 명확함
  • 테스트하기 어려운 코드 특징

    • 외부 상태(DB, 파일, 네트워크)에 의존
    • 내부에서 컬렉션을 직접 생성
    • forEach 안에서 상태 변경
  • 따라서 람다 자체를 테스트하려 하지 않는것이 중요

    • 람다가 사용되는 메서드의 결과를 테스트
    • 중요 로직이면 메서드로 추출
  • 병렬 스트림 테스트 시 주의사항

    • 스트림은 구현이 아니라 결과를 검증
    • 병렬 스트림은 부작용 없는 연산만

4. 디버깅

람다와 스트림을 사용한 코드를 어떻게 디버깅해야 하는지에 대한 기본 접근법을 이해한다

  • 람다·스트림 디버깅이 어려운 이유

    • 람다는 이름이 없음
    • 스트림은 내부 반복(internal iteration)
    • 중간 상태를 직접 볼 수 없음
  • 스트림 디버깅 기본 원칙

    • 스트림 파이프라인을 단계별로 나눠서 생각
    • 필요하면 실제로 분리
  • peek()을 활용한 중간 상태 확인

    • peek()이란? 스트림 요소를 소비하지 않고 중간 연산 과정에서 값을 살펴볼 수 있는 연산
    • peek()는 디버깅 용도. 따라서 로직을 넣으면 안됨.
  • 병렬 스트림은 디버깅 난이도 ↑
    • 실행 순서 비결정적
    • 로그 순서 뒤섞임
    • 따라서 디버깅 중에는 parallelStream() 사용 ❌
    • 문제가 해결되면 병렬 적용 여부 검토