리팩토링 11. 조건문 분해하기
- etc/리팩토링
- 2023. 4. 29.
들어가기 전
이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다.
리팩토링 11. 조건문 분해하기(Decompose Conditional)
- 여러 조건에 따라 달라지는 코드를 작성하다보면 종종 긴 함수가 만들어 지는 것을 볼 수 있음.
- If문의 '조건'과 If문의 '액션' 모두 '의도'를 표현해야 한다.
- 기술적으로는 '함수 추출하기'와 동일한 리팩토링이다. 하지만 함수로 추출해서 If문의 각 부분이 '의미'를 잘 전달할 수 있는데 초점을 맞춘다.
조건문은 어느 순간 아주 복잡해 질 수도 있다. 그렇다면 조건문은 어떤 경우에 많이 복잡해 질 것인가?
- 조건 자체가 복잡해 질 수 있음.
- 각 조건을 만족했을 때, 해야할 일 자체가 길어질 수도 있다.
조건문이 세부 구현 사항과 강하게 결합하게 되면, 코드를 읽는 사람은 더욱 많은 수고를 들여야 읽을 수 있다. 따라서 조건문 분해하기는 '함수 추출'을 통해서 '조건과 액션'에 대해서 읽는 사람이 충분히 알아들을 수 있도록 메서드 이름으로 '의미'를 부여해주는 작업이다.
Before
아래 코드에서 findParticipant() 메서드의 조건문을 살펴보자. 해당 조건문의 조건 / 행동과 관련된 코드들이 있는데, 각 코드들이 실행되었을 때 무슨 일을 하는지에 대한 의미를 알 수가 없다. 이런 부분은 가독성을 해치는 코드이기 때문에 리팩토링을 통해 해결해야 한다. 아래와 같은 형태로 리팩토링 해볼 수 있다.
- 조건문 / 조건을 만족했을 때의 행동 코드를 모두 메서드로 추출한 후, 적절한 이름을 붙여준다.
- 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));
}
'etc > 리팩토링' 카테고리의 다른 글
리팩토링 13. 조건문을 다형성으로 바꾸기 (0) | 2023.04.29 |
---|---|
리팩토링 12. 반복문 쪼개기 (0) | 2023.04.29 |
리팩토링 10. 함수를 명령으로 바꾸기 (0) | 2023.04.29 |
리팩토링 9. 객체 통째로 넘기기 (0) | 2023.04.29 |
리팩토링 8. 매개변수 객체 만들기 (0) | 2023.04.29 |