9장

1. 리팩터링

코드 가독성이 좋다는 건 처음 접하는 사람도 이해하기 쉽게 작성된 코드라는 말이다. 리팩터링은 코드를 간결하고 이해하기 쉽게 만드는 것이다. 람다, 메서드 참조, 스트림을 활용하면 코드 가독성을 높일 수 있다.

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

하나의 추상 메서드를 구현하는 익명 클래스는 람다 표현식으로 리팩터링 할 수 있다.

// 익명 클래스 활용
Runnable r1 = new Runnable() {
    public void run() {
        System.out.println("Hello");
    }
};

// 람다 표현식 활용
Runnable r2 = () -> System.out.println("Hello");

익명 클래스를 람다 표현식으로 변환할 때는 몇 가지 주의점이 있다. 첫째로 this와 super는 익명 클래스와 람다 클래스에서 의미가 다르다. 익명 클래스에서 this는 자신을 가리키지만, 람다 표현식에서 this는 람다를 감싸는 클래스를 가리킨다. 둘째로 익명 클래스는 감싸고 있는 변수를 가릴 수 있지만, 람다 표현식은 변수를 가릴 수 없다.

// 익명 클래스 활용
Runnable r1 = new Runnable() {
    public void run() {
        int a = 2;
        System.out.println(a);
    }
}

// 람다 표현식 활용
int a = 10;
Runnable r2 = () -> {
    int a = 2; // 컴파일 에러
    system.out.println(a);
}

셋쩨로 익명 클래스를 람다 표현식으로 바꾸면 컨텍스트 오버로딩에 따른 모호함이 일어날 수 있다. 익명 클래스는 인스턴스화 할 때 명시적으로 형식이 정해지는 반면 람다 표현식은 컨텍스트에 따라 달라지기 때문이다.

위 예시처럼 람다 표현식은 doSomething(Runnable)과 doSomething(Task) 중 어느 것을 가리키는지 말 수 없다. 이를 해결하기 위해 명시적 형변환을 사용할 수 있다.

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

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

comparing과 maxBy 같은 정적 헬퍼 메서드를 활용할 수도 있다. sum, maximum 등 자주 사용하는 리듀싱 연산은 메서드 참조와 함께 사용할 수 있는 내장 헬퍼 메서드를 제공한다.

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

명령형 데이터 처리를 스트림으로 리팩터링할 수 있다.

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

디자인 패턴(design pattern)은 공통적인 소프트웨어 문제를 설계할 때 재사용할 수 있는 검증된 청사진을 말한다. 예를 들어 구조체와 동작하는 알고리즘을 분리하고 싶을 때 방문자 디자인 패턴(visitor design pattern)을 사용할 수 있다. 또한 클래스 인스턴스를 하나의 객체로 제한하기 위해 싱글톤 패턴(singleton pattern)을 사용할 수 있다.

2.1. 전략

전략 패턴(strategy pattern)은 한 유형의 알고리즘을 보유한 상태에서 런타임에 적절한 알고리즘을 선택하는 기법이다. 전략 패턴은 세 부분으로 구성된다.

  • 전략 객체를 사용하는 한 개 이상의 클라이언트

  • 알고리즘을 나타내는 전략 인터페이스

  • 다양한 알고리즘을 나타내는 한 개 이상의 인터페이스 구현

png

람다 표현식을 활용하면 전략 디자인 패턴에서 발생하는 자잘한 코드를 제거할 수 있다.

2.2. 템플릿 메서드

템플릿 메서드 패턴(template method pattern)은 알고리즘의 개요를 만든 다음 알고리즘의 일부를 수정하는 기법이다.

람다 표현식을 활용하면 템플릿 메서드 디자인 패턴에서 발생하는 자잘한 코드를 제거할 수 있다.

2.3. 옵저버

옵저버 패턴(observer pattern)은 어떤 이벤트가 발생했을 때 한 객체(subject)가 다른 객체 리스트(observer)에 자동으로 알림을 보내는 기법이다. 옵저버 패턴으로 커스터마이즈 된 알림 시스템을 설계하고 구현할 수 있다.

png

람다 표현식을 활용하면 옵저버 패턴에서 발생하는 자잘한 코드를 제거할 수 있다.

2.4. 책임 연쇄

책임 연쇄 패턴(chain of responsibility pattern)은 작업 처리 객체의 체인을 만드는 기법이다. 한 객체가 어떤 작업을 처리한 다음 다른 객체로 결과를 전달하고, 다른 객체로 작업을 처리한 다음 또 다른 객체로 전달한다.

png

람다 표현식을 활용하면 책임 연쇄 패턴에서 발생하는 자잘한 코드를 제거할 수 있다.

2.5. 팩토리

팩토리 패턴(factory pattern)은 인스턴스화 로직을 클라이언트에 노출하지 않고 객체를 만드는 기법이다.

생성자 참조를 활용하면 팩토리 패턴에서 발생하는 자잘한 코드를 제거할 수 있다.

3. 람다 테스팅

그래픽 애플리케이션의 일부인 Point 클래스가 있다고 생각해보자.

람다는 익명 함수이므로 테스트 코드 이름을 호출할 수 없다. 그러나 람다 표현식은 함수형 인터페이스의 인스턴스를 생성하므로 생성된 인스턴스의 동작으로 람다 표현식을 테스트할 수 있다.

4. 디버깅

4.1. 스택 트레이스

코드를 실행하면 다음과 같은 스택 트레이스가 출력된다.

람다 표현식은 이름이 없기 때문에 컴파일러가 람다를 참조하는 이름을 만들어낸다. 메서드 참조를 사용해도 스택 트레이스에는 메서드 명이 나타나지 않는다. 참조하는 메서드가 동일한 위치에 선언되어 있어야만 메서드 참조 이름이 스택 트레이스에 출력된다.

4.2. 로깅

forEach를 호출하면 전체 스트림이 소비된다.

peek 스트림 연산을 활용해서 소비 없이 스트림 파이프라인 연산을 디버깅할 수 있다. peek은 각 원소를 소비한 것처럼 동작하지만 실제로 스트림의 원소를 소비하지는 않는다. peek은 자신이 확인한 원소를 다음 파이프라인으로 전달한다.

참고 자료

  • 모던 자바 인 액션 - 한빛미디어

Last updated