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

    들어가기 전

    이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다. 


    리팩토링 33. 반복문을 파이프라인으로 바꾸기 (Replace Loop with Pipeline)

    • Collection 파이프 라인 (자바의 Stream)
    • 고전적인 반복문을 파이프라인 오퍼레이션을 사용해 표현하면 코드를 더 명확하게 만들 수 있다. (가독성 향상 도모)
      • 필터 (filter): 전달받은 조건의 true에 해당하는 데이터만 다음 오퍼레이션으로 전달. 
      • 맵 (map): 전달받은 함수를 사용해 입력값을 원하는 출력값으로 변환하여 다음 오퍼레이션으로 전달.
    • https://martinfowler.com/articles/refactoring-pipelines.html (더 많은 예시)

    '반복문을 파이프라인으로 바꾸기' 리팩토링은 고전적으로 사용되는 반복문을 파이프라인으로 바꾸면서 가독성 개선을 도모하는 리팩토링이다. 이런 작업은 아래 형태로 변환하면서 사용할 수 있다. 

    • If-Else 문 → Filter() 메서드로 대체
    • 객체의 변환 → Map() 메서드로 대체 

    어떤 관점에서 더 읽기 쉬워지는 걸까? 기본적으로 For 문안에 If나 Else가 Nested되는 경우, 코드를 점점 읽기 어려워지는 경우가 많다. 스트림을 사용하게 되면, 그런 부분들에 대해서는 한결 더 읽기 쉬워진다. 


    코드 살펴보기

    아래 Author 클래스의 TwitterHandles() 메서드를 살펴보자. 이 메서드에서는 for문이 있는데, for문 자체는 문제가 없다. 다만 for문에서 사용되는 로직이 일반적이기 때문에 for문을 Stream으로 바꾸면 가독성이 좋아질 수 있다는 것이다. 아래 코드는 일단은 Nested If문이 여러번 사용되어서 집중해서 읽지 않으면, TwitterHandle() 코드가 하는 일에 대해서 이해하기가 어렵다. 

     

     

    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. 첫번째 If문 → Filter() 메서드를 이용해서 변경
    2. 변수할당 → Map()을 이용해 처리할 수 있음.
    3. 두번째 If문 → Filter() 메서드를 이용해 변경. 이 때, Mapping된 변수를 전달.
    4. result.add() → Collection을 만드는 작업이기 때문에 Collect() 메서드를 이용해 처리.

    이 순서대로 리팩토링을 진행하면 아래 코드처럼 변경되게 된다. filter / map / collect 메서드가 무엇을 하는지 알고 있다면 아래 코드는 더 읽기 쉬워진 코드가 된다. 

    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) {
            // 아래 반복문을 파이프라인으로 바꾸기
            return authors.stream()
                    .filter(author -> author.company.equals(company))
                    .map(author -> author.twitterHandle)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
    
        }
    
    }

    댓글

    Designed by JB FACTORY