리팩토링 4. 함수 추출하기
- etc/리팩토링
- 2023. 4. 27.
들어가기 전
이 글은 인프런 백기선님의 리팩토링 강의를 복습하며 작성한 글입니다.
리팩토링 4. 함수 추출하기 (Extract Function)
- 함수 추출의 기준은 '의도'와 '구현'을 분리할 때 사용함.
- 무슨 일을 하는 코드인지 알아내려고 노력해야 하는 코드라면 해당 코드를 함수로 분리하고 함수 이름으로 '무슨 일을 하는지' 표현할 수 있다.
- 무슨 일을 하는 코드인지 알아내려고 노력해야 하는 코드라면 이 코드는 구현에 집중한 코드다.
- 구현에 집중한 코드는 '의도'를 표현하는 메서드 이름을 사용한 메서드로 추출한다.
- 한줄 짜리 메서드도 괜찮은가?
- 괜찮음.
- 거대한 함수 안에 들어있는 주석은 추출할만한 함수를 찾는데 있어서 좋은 단서가 될 수 있다.
함수 추출하기 자체는 '중복 코드' 냄새를 제거하기 위한 용도뿐만 아니라 다양한 관점의 냄새를 제거하는데 사용될 수 있다. 하지만 이번 장에서는 '중복 코드' 제거 관점만 살펴보고자 한다.
함수 추출의 기준은? → 의도 / 구현
함수 추출의 기준은 사람마다 제각각이다. 하지만 책에서는 '의도'를 표현한 코드와 '구현'를 표현한 코드 관점에서 바라보도록 한다. 하지만 이 기준 자체도 주관적이긴 하다는 점을 미리 인지해야한다. 그럼 의도 / 구현을 표현한 코드는 각각 무엇을 의미할까?
구현을 표현한 코드
- 어떤 코드를 읽었을 때, 무슨 일을 하는지 잘 표현하지 못하는 코드
- 어떤 코드를 읽었을 때, 무슨 일을 하는지 이해하는데 시간이 걸리는 코드
위 코드들은 '구현'에 집중한 코드라고 이해할 수 있다. 구현에 집중한 코드들은 메서드 추출의 대상이 된다.
의도를 표현한 코드
- 코드가 책처럼 읽히면서 어떤 일을 하는지 정확히 표현하는 경우
이 코드들은 구현보다는 의미를 잘 표현한 코드가 될 것이다.
JDBC의 실행을 예시로 든다면 메서드가 다음 코드로만 구성되어 있다면, 굉장히 의도를 잘 표현한 코드가 될 것이다.
- getConnection (커넥션 가져오기)
- createStatements (statement 만들기)
- doExecute (쿼리 실행하기)
- returnValue (결과값 반환하기)
한줄 짜리 메서드도 괜찮은가?
'구현'에 집중한 코드를 '의도'를 잘 표시하기 위해 메서드로 추출하고 이름으로 '의도'를 표현해준다면 한줄짜리 메서드를 추출하는 것도 좋은 방법이 될 수 있다.
거대한 함수 안에 들어있는 주석은 추출한 함수를 찾는데 있어서 좋은 단서가 될 수 있다.
함수 내부에 존재하는 주석은 종종 이 코드가 하는 일을 나타낸다. 만약 이런 주석이 있다면, 관련된 코드를 주석을 이름으로 하는 메서드로 추출하는 것도 좋다. 왜냐하면 주석은 '의미'를 나타내고, 코드는 '구현'을 나타내고 있기 때문이다.
리팩토링 with Extract Function (Before)
아래에서 Extract Function 기법을 이용해서 해볼만한 것을 찾아보자.
- 메서드 내부가 전반적으로 잘 읽히지 않는다 → 각 코드들은 구현에 치중한 코드다.
- 메서드 내부에 주석이 있다. → 의미를 잘 나타내어준다.
아래 코드의 전반적인 문제점은 작성된 코드가 대부분 구현을 나타내기 때문에 코드 자체가 하는 일을 이해하기가 상대적으로 어렵다는 것이다. 따라서 '구현'에 집중한 부분을 '의미'에 집중하도록 메서드 단위로 추출한 후, 메서드의 이름을 명시해주는 것으로 해결해 볼 수 있다.
public class StudyDashboard {
// 1. 각 주석은 '의미'에 가깝다. 따라서 '구현'에 가까운 코드를 '주석' 이름으로 메서드로 추출하면 의미로 표현할 수 있다.
// 2. 한 줄 짜리 메서드도 '구현'을 '의미'로 나타낼 수 있다면 바꾸는 것도 좋다.
private void printParticipants(int eventId) throws IOException {
// Get github issue to check homework
GitHub gitHub = GitHub.connect();
GHRepository repository = gitHub.getRepository("whiteship/live-study");
GHIssue issue = repository.getIssue(eventId);
// Get participants
Set<String> participants = new HashSet<>();
issue.getComments().forEach(c -> participants.add(c.getUserName()));
// Print participants
participants.forEach(System.out::println);
}
private void printReviewers() throws IOException {
// Get github issue to check reviews
GitHub gitHub = GitHub.connect();
GHRepository repository = gitHub.getRepository("whiteship/live-study");
GHIssue issue = repository.getIssue(30);
// Get reviewers
Set<String> reviewers = new HashSet<>();
issue.getComments().forEach(c -> reviewers.add(c.getUserName()));
// Print reviewers
reviewers.forEach(System.out::println);
}
public static void main(String[] args) throws IOException {
StudyDashboard studyDashboard = new StudyDashboard();
studyDashboard.printReviewers();
studyDashboard.printParticipants(15);
}
}
리팩토링 with Extract Function (After)
'구현'을 '의미'로 나타낼 수 있도록 메서드를 추출했다.
- 주석을 이름으로 사용한 메서드를 추출했다.
- 한줄 짜리 메서드라도 이름을 이용해서 하는 일을 좀 더 명시적으로 수정했다.
기본적으로 메서드는 3개의 덩어리로 나누어져 있었고, 각 덩어리는 주석으로 어떤 일을 하는지 표시되어 있었다. 주석 이름을 이용한 메서드를 생성해서, 각 덩어리가 무슨 일을 하는지 더욱 명확히 표현해주었다.
마지막으로 forEach()를 하는 한 줄 짜리 코드도 메서드로 추출했다. forEach()만으로는 코드가 수행하는 작업의 의미가 명확하지 않기 때문에 메서드로 따로 추출하고, 메서드가 하는 일을 이름으로 알려주었다.
public class StudyDashboard {
private void printParticipants(int eventId) throws IOException {
GHIssue issue = getGithubIssue(eventId);
Set<String> participants = getUserNames(issue);
print(participants);
}
private void printReviewers() throws IOException {
GHIssue issue = getGithubIssue(30);
Set<String> reviewers = getUserNames(issue);
print(reviewers);
}
private static void print(Set<String> participants) {
participants.forEach(System.out::println);
}
private Set<String> getUserNames(GHIssue issue) throws IOException {
Set<String> participants = new HashSet<>();
issue.getComments().forEach(c -> participants.add(c.getUserName()));
return participants;
}
private GHIssue getGithubIssue(int eventId) throws IOException {
GitHub gitHub = GitHub.connect();
GHRepository repository = gitHub.getRepository("whiteship/live-study");
return repository.getIssue(eventId);
}
public static void main(String[] args) throws IOException {
StudyDashboard studyDashboard = new StudyDashboard();
studyDashboard.printReviewers();
studyDashboard.printParticipants(15);
}
}
'etc > 리팩토링' 카테고리의 다른 글
리팩토링 17. 변수 캡슐화하기 (0) | 2023.04.27 |
---|---|
리팩토링 : 냄새 5 - 전역 데이터 (0) | 2023.04.27 |
리팩토링6. 메서드 올리기 (0) | 2023.04.27 |
리팩토링 5. 코드 정리하기 (0) | 2023.04.27 |
리팩토링 냄새 2. 중복코드 (0) | 2023.04.27 |