DEV ℧ Developer Diary

[Refactoring] 냄새 13. 반복문

해당 포스트는 inflearn의 백기선님의 강의인 리팩토링 을 듣고 정리한 글입니다.

냄새 13. 반복문

  • 프로그래밍 언어 초기부터 있었던 반복문은 처음엔 별다른 대안이 없어서 간과했지만 최근 Java와 같은 언어에서 함수형 프로그래밍을 지원하면서 반복문에 비해 더 나은 대안책이 생겼다.
  • “반복문을 파이프라인으로 바꾸는 (Replace Loop with Pipeline)” 리팩토링을 적용하면 필터나 맵핑과 같은 파이프라인 기능을 사용해 보다 빠르게 어떤 작업을 하는지 파알할 수 있다.

리팩토링 33. 반복문을 파이프라인으로 바꾸기

  • 콜렉션 파이프라인 (자바의 Stream, C#의 LINQ - Language Integrated Query)
  • 고전적인 반복문을 파이프라인 오퍼레이션을 사용해 표현하면 코드를 더 명확하게 만들 수 있다.
    • 필터 (filter) : 전달받은 조건의 true에 해당하는 데이터만 다음 오퍼레이션으로 전달.
    • 맵 (map) : 전달받은 함수를 사용해 입력값을 원하는 출력값으로 변환하여 다음 오퍼레이션으로 전달.
  • https://martinfowler.com/articles/refactoring-pipelines.html : 해당 URL에서 여러 예시를 찾아볼 수 있다.

아래의 Author 객체에 있는 반복문을 Stream API 를 이용해 파이프라인으로 치환하는 작업을 해보도록 하자.

  • Author
public class Author {

    private String company;

    private String twitterHandle;

    public Author(String company, String twitterHandle) {
        this.company = company;
        this.twitterHandle = twitterHandle;
    }

    static public List<String> TwitterHandles(List<Author> authors, String company) {
        var result = new ArrayList<String> ();
        for (Author a : authors) {
            if (a.company.equals(company)) {
                var handle = a.twitterHandle;
                if (handle != null)
                    result.add(handle);
            }
        }
        return result;
    }

}

테스트코드를 돌려보면 정상으로 돌아가는것을 확인할 수 있다.

리팩토링1

그리고 Author 객체의 변환하고자 하는 메소드 TwitterHandles 의 로직을 확인해보자.

  1. List<Author>를 매개변수로 받아. 반복문을 돌려준다.
  2. 첫번째 조건으로, Author 객체 내부의 company 변수와 동일한지 판별한다.
  3. 첫번째 조건에 부합할 경우, Author 객체에서 twitterHandle 값을 꺼낸다.
  4. 두번째 조건으로, 꺼낸 twitterHandle 값이 null이 아닌지 판별한다.
  5. 두번째 조건에 부합할 경우, List에 담아 반환한다.

Stream API에서 조건은 filter를 이용하고 데이터의 가공은 map을 이용한다.

Stream을 통해 변경된 소스는 아래와 같다.

static public List<String> TwitterHandles(List<Author> authors, String company) {
  return authors.stream()                           /* 매개변수로 받은 author을 stream(반복문)통해 가공한다. */
          .filter(a -> a.company.equals(company))   /* Author객체 내부의 company 변수와 동일한지 판별 */
          .map(a -> a.twitterHandle)                /* Author 객체에서 twitterHandle 값을 꺼낸다. */
          .filter(t -> t != null)                   /* 꺼낸 `twitterHandle` 값이 null이 아닌지 판별한다. */
          .collect(Collectors.toList());            /*List에 담아 반환한다.*/
}

다시 테스트를 돌려봐 정상적으로 돌아가는지 확인해 보자.

리팩토링1