리팩토링 11. 조건문 분해하기

    들어가기 전

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


    리팩토링 11. 조건문 분해하기(Decompose Conditional)

    • 여러 조건에 따라 달라지는 코드를 작성하다보면 종종 긴 함수가 만들어 지는 것을 볼 수 있음. 
    • If문의 '조건'과 If문의 '액션' 모두 '의도'를 표현해야 한다.
    • 기술적으로는 '함수 추출하기'와 동일한 리팩토링이다. 하지만 함수로 추출해서 If문의 각 부분이 '의미'를 잘 전달할 수 있는데 초점을 맞춘다. 

     

    조건문은 어느 순간 아주 복잡해 질 수도 있다. 그렇다면 조건문은 어떤 경우에 많이 복잡해 질 것인가? 

    • 조건 자체가 복잡해 질 수 있음.
    • 각 조건을 만족했을 때, 해야할 일 자체가 길어질 수도 있다. 

    조건문이 세부 구현 사항과 강하게 결합하게 되면, 코드를 읽는 사람은 더욱 많은 수고를 들여야 읽을 수 있다. 따라서 조건문 분해하기는 '함수 추출'을 통해서 '조건과 액션'에 대해서 읽는 사람이 충분히 알아들을 수 있도록 메서드 이름으로 '의미'를 부여해주는 작업이다. 


    Before

    아래 코드에서 findParticipant() 메서드의 조건문을 살펴보자. 해당 조건문의 조건 / 행동과 관련된 코드들이 있는데, 각 코드들이 실행되었을 때 무슨 일을 하는지에 대한 의미를 알 수가 없다. 이런 부분은 가독성을 해치는 코드이기 때문에 리팩토링을 통해 해결해야 한다. 아래와 같은 형태로 리팩토링 해볼 수 있다. 

    1. 조건문 / 조건을 만족했을 때의 행동 코드를 모두 메서드로 추출한 후, 적절한 이름을 붙여준다.
    2. If / Else만으로 이루어져 있기 때문에 삼항 연산자로 간추려서 사용할 수 있다. 
    private Participant findParticipant(String username, List<Participant> participants) {
        Participant participant;
        // 무슨 조건인지 이해하기 어렵다.
        if (participants.stream().noneMatch(p -> p.username().equals(username))) {
            // 무슨 행동인지 이해하기 어렵다.
            participant = new Participant(username);
            participants.add(participant);
        } else {
            // 무슨 행동인지 이해하기 어렵다.
            participant = participants.stream().filter(p -> p.username().equals(username)).findFirst().orElseThrow();
        }
    
        return participant;
    }

    After

    위의 내용을 바탕으로 리팩토링을 진행했다. 리팩토링 결과는 아래와 같다.

    • 조건과 행동을 각각 메서드로 추출한 후, 이름을 통해 명확한 의미를 전달하도록 수정함.
    • 삼항 연산자를 사용해 한 줄의 코드로 수정함. 
    private Participant findParticipant(String username, List<Participant> participants) {
        return isNewParticipant(username, participants) ?
                createNewParticipant(username, participants) :
                findExistingParticipant(username, participants);
    }
    
    private Participant findExistingParticipant(String username, List<Participant> participants) {
        Participant participant;
        participant = participants.stream().filter(p -> p.username().equals(username)).findFirst().orElseThrow();
        return participant;
    }
    
    private Participant createNewParticipant(String username, List<Participant> participants) {
        Participant participant;
        participant = new Participant(username);
        participants.add(participant);
        return participant;
    }
    
    private boolean isNewParticipant(String username, List<Participant> participants) {
        return participants.stream().noneMatch(p -> p.username().equals(username));
    }

     


    FuthreMore

    + 로 생각해보면 isNewParticipant(), createNewParticipant() 등을 살펴보면 매개변수로 username, participants가 같이 전달되는 것을 볼 수 있다. 이것은 어떻게 보면 '의미 관계를 가지는 매개변수'로 이해할 수 있고, 필요하다면 Record 같은 것으로 하나로 묶어서 제공하는 방법도 좋을 것이다. 

    private Participant findParticipant(String username, List<Participant> participants) {
        return isNewParticipant(username, participants) ?
                createNewParticipant(username, participants) :
                findExistingParticipant(username, participants);
    }
    
    private Participant findExistingParticipant(String username, List<Participant> participants) {
        Participant participant;
        participant = participants.stream().filter(p -> p.username().equals(username)).findFirst().orElseThrow();
        return participant;
    }
    
    private Participant createNewParticipant(String username, List<Participant> participants) {
        Participant participant;
        participant = new Participant(username);
        participants.add(participant);
        return participant;
    }
    
    private boolean isNewParticipant(String username, List<Participant> participants) {
        return participants.stream().noneMatch(p -> p.username().equals(username));
    }

     

    댓글

    Designed by JB FACTORY