반응형
임시 변수를 질의 함수로 바꾸기 (Replace Parameter with Query)
- 함수의 매개변수 목록은 함수의 다양성을 대변하며, 짧을수록 이해하기 좋다.
- 어떤 한 매개변수를 다른 매개변수를 통해 알아낼 수 있다면 중복 매개변수라 생각할 수 있다.
- 매개변수에 값을 전달하는 것은 함수를 호출하는 쪽의 책임이다. 가능하면 함수를 호출하는 쪽의 책임을 줄이고 함수 내부에서 책임지도록 노력한다.
- 임시 변수를 질의 함수로 바꾸기와 함수 선언 변경하기를 통해 이 리팩토링을 적용한다.
예시
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);
});
냄새
- 위 예제 코드 print() 메서드의 마지막 부분이다.
- rate를 계산해주기 위한 일련의 로직이 길게 작성되어 있으며 그 과정에 있어 임시 변수를 사용한다. 또한 formating 해주는 부분을 읽기 힘들다
- 위 코드는 의도를 표현한 것이 아닌 구현을 표현한 코드이다. 리팩토링을 해줄 필요가 있다.
해결
- 임시 변수를 함수화 하자!
- 소드를 옮기고, 데이터를 필드로 만들면 메소드에 전달해야 하는 매개변수 목록도 줄일 수 있다.
매개변수 객체 만들기
Introduce Parameter Object
- 같은 매개변수들이 여러 메소드에 걸쳐 나타난다면 그 매개변수들을 묶은 자료 구조를 만들 수 있다.
- 그렇게 만든 자료구조
- 해당 데이터간의 관계를 보다 명시적으로 나타낼 수 있다.
- 함수에 전달할 매개변수 개수를 줄일 수 있다.
- 도메인을 이해하는데 중요한 역할을 하는 클래스로 발전할 수도 있다
예시
public String getMarkdownForParticipant(int totalNumberOfEvents, Participant p) {
....
}
private String header(int totalNumberOfEvents, int totalNumberOfParticipants) {
....
}
냄새
- 반복되는 인자를 갖는 함수들이다.
- 위의 설명처럼 같은 매개변수들이 여러 메소드에 걸쳐 나타난다면 그 매개변수들을 묶은 자료구조를 만들 수 있다.
해결
- record 와 같은 자료구조를 만들어 해결한다.
- 함수 내부에서 자주 쓰이는 공통적인 매개변수를 필드로 바꾼다.
객체 통째로 넘기기
Preserve Whole Object
- 어떤 한 레코드에서 구할 수 있는 여러 값들을 함수에 전달하는 경우, 해당 매개변수를 레코드 하나로 교체할 수 있다.
- 매개변수 목록을 줄일 수 있다.
- 이 기술을 적용하기 전에 의존성을 고려해야 한다.
- 어쩌면 해당 메소드의 위치가 적절하지 않을 수도 있다
예시
private String getMarkdownForParticipant(String username, Map<Integer, Boolean> homework) {
return String.format("| %s %s | %.2f%% |\n", username,
checkMark(homework, this.totalNumberOfEvents),
getRate(homework));
}
냄새
- 매개변수를 객체화할 수 있다.
해결
- 위의 코드의 매개변수를 Participant 객체로 만들어 매개변수를 줄인다.
- 더 나아가서 내부의 getRate() 함수도 Participant 내부에서 사용한다.
함수를 명령으로 바꾸기
Replace Function with Command
- 함수를 독립적인 객체인, Command로 만들어 사용할 수 있다.
- 커맨드 패턴을 적용하면 다음과 같은 장점을 취할 수 있다.
- 부가적인 기능으로 undo 기능을 만들 수도 있다.
- 더 복잡한 기능을 구현하는데 필요한 여러 메소드를 추가할 수 있다.
- 상속이나 템플릿을 활용할 수도 있다.
- 복잡한 메소드를 여러 메소드나 필드를 활용해 쪼갤 수도 있다.
- 대부분의 경우에 “커맨드” 보다는 “함수”를 사용하지만, 커맨드 말고 다른 방법이 없는 경우에만 사용한다.
예시
private void print() throws IOException, InterruptedException {
GitHub gitHub = GitHub.connect();
GHRepository repository = gitHub.getRepository("whiteship/live-study");
List<Participant> participants = new CopyOnWriteArrayList<>();
ExecutorService service = Executors.newFixedThreadPool(8);
CountDownLatch latch = new CountDownLatch(totalNumberOfEvents);
for (int index = 1 ; index <= totalNumberOfEvents ; index++) {
int eventId = index;
service.execute(new Runnable() {
@Override
public void run() {
try {
GHIssue issue = repository.getIssue(eventId);
List<GHIssueComment> comments = issue.getComments();
for (GHIssueComment comment : comments) {
String username = comment.getUserName();
boolean isNewUser = participants.stream().noneMatch(p -> p.username().equals(username));
Participant participant = null;
if (isNewUser) {
participant = new Participant(username);
participants.add(participant);
} else {
participant = participants.stream().filter(p -> p.username().equals(username)).findFirst().orElseThrow();
}
participant.setHomeworkDone(eventId);
}
latch.countDown();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
});
}
latch.await();
service.shutdown();
try (FileWriter fileWriter = new FileWriter("participants.md");
PrintWriter writer = new PrintWriter(fileWriter)) {
participants.sort(Comparator.comparing(Participant::username));
writer.print(header(participants.size()));
participants.forEach(p -> {
String markdownForHomework = getMarkdownForParticipant(p.username(), p.homework());
writer.print(markdownForHomework);
});
}
}
냄새
- print() 함수 내부에서 실질적으로 print를 해주는 로직을 객체화하여 사용하면 깔끔해지지 않을까?
해결
- 새로운 클래스를 생성 후 해당 클래스 내부에서 연관된 함수를 구현한다.
참고
'Language > Java' 카테고리의 다른 글
긴 매개변수 목록 (0) | 2022.08.15 |
---|---|
긴 함수(2) (0) | 2022.08.14 |
Optional (0) | 2022.07.02 |
Java8 Stream (0) | 2022.07.02 |
인터페이스 기본 메소드와 스태틱 메소드 (0) | 2022.06.19 |