리팩토링 7. 임시 변수를 질의 함수로 바꾸기

    들어가기 전

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


    리팩토링 7. 임시 변수를 질의 함수로 바꾸기 (Replace Temp With Query)

    • 임시변수 사용은 다음 장점을 가짐.
      • 반복해서 동일한 식을 계산하는 것을 피할 수 있음.
      • 이름을 사용해 의미를 표현할 수도 있음.
    • 긴 함수를 리팩토링 할 때, 임시 변수를 함수 추출하기로 분리한다면 '함수 추출하기'로 리팩토링한 함수에 전달되어야 할 매개변수를 줄일 수 있음. 

    임시변수의 장점

    메서드 내부에서 사용되는 값들이 있는데, 이런 값들은 임시변수를 이용해서 명시해주는 것이 좋다. 좋은 이유는 두 가지다.

    1. 반복되는 계산을 하지 않아도 된다. 
    2. 변수가 이름을 가지기 때문에 의미를 잘 표현할 수 있다. 

    따라서 반복되는 수식이라면 임시변수를 생성하는 것 역시 하나의 '리팩토링'으로 볼 수 있다. 그런데 긴 함수에서 너무 많은 임시변수가 생기면, 정작 함수 추출하기를 할 때 생성되는 함수의 매개변수의 숫자가 많아질 수 있다는 것이다. 함수의 매개변수가 많아지는 것 역시 경계해야 할 부분이다. 

    임시변수를 사용하면서, 함수 추출하기의 매개변수를 줄일 수 있는 방법은 없을까? 


    임시 변수를 질의 함수로 추출하자

    질의 함수라는 것은 임시 변수를 리턴해주는 함수를 의미한다. 다음 조건을 만족하면 함수 추출하기로 생성되는 함수에 새로운 매개변수를 추가하지 않아도, 생성된 함수에서 임시변수를 사용할 수 있다. 

    함수 추출하기에서 생성된 함수의 매개변수로 질의 함수를 실행할 수 있는 경우 

    어떤 내용인지는 글만 보면 잘 이해가 되지 않으니, 코드로 확인해보자.


    실습 (Before)

    String.format 코드를 리팩토링 해보고자 한다. 

    1. String.format 코드는 긴 코드다. 왜냐하면 세부 구현에만 집중되어 있는 코드이기 때문에 언듯 보았을 때, 무슨 일을 하는지 전혀 알 수 없다. 따라서 리팩토링 대상이 된다.
    2. String.format을 함수 추출하기로 추출하는 경우, 전달해야하는 매개변수가 3개다. 매개변수도 제법 많아진다. 

    String.format() 함수에는 totalNumberOfEvents, p, rate 라는 매개변수가 넘어가게 된다. 여기서 한 가지 살펴볼 부분은 임시변수 rate다. 

    임시변수 rate는 특정 수식을 통해서 계산이 되는데, 계산될 때 필요한 값은 totalNumberOfEvents, p만 있으면 된다. 즉, 함수 추출하기로 만들어진 함수 getMarkdownForParticipant()가 요구하는 매개변수로만 만들어 질 수 있다. 따라서 rate를 getRate() 메서드로 추출한 다음에 getMarkdownForParticipant()에서 getRate()를 호출해서 rate를 참조하는 형식으로 구현할 수 있다. 

    따라서 리팩토링은 다음과 같이 해야한다.

    1. getMarkdownForParticipant() 메서드를 생성함. 이것은 String.format()의 긴 함수를 의미있는 이름으로 바꾸어 보다 짧은 함수로 바꾸는 작업임.
    2. 쿼리 메서드 getRate()를 추출해서 getMarkdownForParticipant()에서 호출하도록 한다. 이를 통해 매개변수 rate를 getMarkdwonForParticipant()에 전달하지 않도록 한다. 
    private void print() throws IOException, InterruptedException {
        
        ...
    
        try (FileWriter fileWriter = new FileWriter("participants.md");
             PrintWriter writer = new PrintWriter(fileWriter)) {
            participants.sort(Comparator.comparing(Participant::username));
    
            writer.print(header(totalNumberOfEvents, participants.size()));
    
            participants.forEach(p -> {
                long count = p.homework().values().stream()
                        .filter(v -> v == true)
                        .count();
    
                double rate = count * 100 / totalNumberOfEvents;
    
                // 이 부분을 리팩토링 할 수 있다.
                // 한 줄 짜리 코드지만 구현에 집중해있어서, 코드만 봤을 때는 무엇을 하는지 전혀 알 수 없다.
                String markdownForHomework = String.format("| %s %s | %.2f%% |\n", p.username(), checkMark(p, totalNumberOfEvents), rate);
                writer.print(markdownForHomework);
            });
        }
    }

     

    실습 (After)

    위의 리팩토링을 반영하면 아래와 같은 결과를 확인할 수 있다.

        ...
        try (FileWriter fileWriter = new FileWriter("participants.md");
             PrintWriter writer = new PrintWriter(fileWriter)) {
            participants.sort(Comparator.comparing(Participant::username));
    
            writer.print(header(totalNumberOfEvents, participants.size()));
    
            participants.forEach(p -> {
                // 함수 추출
                double rate = getRate(totalNumberOfEvents, p); 
    
                // 이 부분을 리팩토링 할 수 있다.
                // 한 줄 짜리 코드지만 구현에 집중해있어서, 코드만 봤을 때는 무엇을 하는지 전혀 알 수 없다.
                // 함수로 추출했더니, 전달되는 매개변수가 3개다. 좀 더 줄이는 것이 좋다.
                // String markdownForHomework = getMarkdownForParticipant(totalNumberOfEvents, p, rate);
                String markdownForHomework = getMarkdownForParticipant(totalNumberOfEvents, p);
                writer.print(markdownForHomework);
            });
        }
    }
    
    // 임시변수를 위한 쿼리 함수 생성
    private double getRate(int totalNumberOfEvents, Participant p) {
        long count = p.homework().values().stream()
                .filter(v -> v == true)
                .count();
        return count * 100 / totalNumberOfEvents;
    }
    
    // 함수 추출
    private String getMarkdownForParticipant(int totalNumberOfEvents, Participant p) {
        return String.format("| %s %s | %.2f%% |\n",
                p.username(),
                checkMark(p, totalNumberOfEvents),
                getRate(totalNumberOfEvents, p));
    }

    댓글

    Designed by JB FACTORY