| 인프런 - 백기선님의 코딩으로 학습하는 리팩토링 강의를 수강하며 정리한 글입니다.
긴 매개변수 목록
- 함수에 매개변수가 많을수록 함수의 역할을 이해하기 힘들어진다.
- 어떤 매개변수를 다른 매개변수를 통해 알아낼 수 있다면, 매개변수를 질의 함수로 바꾸기를 사용할 수 있다.
- 기존 자료구조에서 세부적인 데이터를 가져와서 여러 매개변수로 넘기는 대신, 객체 통째로 넘기기를 사용할 수 있다.
- 일부 매개변수들이 대부분 같이 넘겨진다면, 매개변수 객체 만들기를 사용할 수 있다.
- 매개변수가 플래그로 사용된다면, 플래그 인수 제거하기를 사용할 수 있다.
- 여러 함수가 일부 매개변수를 공통적으로 사용한다면, 여러 함수를 클래로 묶기를 통해 매개변수를 해당 클래스의 필드로 만들고 메서드에 전달해야 할 매개변수 목록을 줄일 수 있다.
Refactoring 1. 매개변수를 질의 함수로 바꾸기
- 함수의 매개변수 목록은 함수의 다양성을 대변, 짧을 수록 좋다.
- 한 매개변수를 다른 매개변수를 통해서 알아낼 수 있다면, 이를 중복 매개변수라 한다.
- 함수를 호출하는 쪽의 책임을 줄이고 함수 내부에서 책임지도록 노력한다.
- 임시 변수를 질의 함수로 바꾸기와 함수 선언 변경하기를 통해 리팩토링을 적용한다.
Before
public double finalPrice() {
double basePrice = this.quantity * this.itemPrice;
int discountLevel = this.quantity > 100 ? 2 : 1;
return this.discountedPrice(basePrice, discountLevel);
}
private double discountedPrice(double basePrice, int discountLevel) {
return discountLevel == 2 ? basePrice * 0.90 : basePrice * 0.95;
}
After
public double finalPrice() {
double basePrice = this.quantity * this.itemPrice;
return this.discountedPrice(basePrice);
}
private int discountLevel() {
return this.quantity > 100 ? 2 : 1;
}
private double discountedPrice(double basePrice) {
return discountLevel() == 2 ? basePrice * 0.90 : basePrice * 0.95;
}
Refactoring 2. 플래그 인수 제거하기
- 플래그는 보통 함수에 매개변수로 전달해서, 함수의 내부 로직을 분기하는데 사용한다.
- 플래그를 사용한 함수는 차이를 파악하기 힘들다.
- 조건문 분해하기를 활용할 수 있다.
- 플래그가 많이 존재한다. -> 해당 함수가 너무 많은 일을 담당하고 있다.
Before
public LocalDate deliveryDate(Order order, boolean isRush) {
if (isRush) {
int deliveryTime = switch (order.getDeliveryState()) {
case "WA", "CA", "OR" -> 1;
case "TX", "NY", "FL" -> 2;
default -> 3;
};
return order.getPlacedOn().plusDays(deliveryTime);
} else {
int deliveryTime = switch (order.getDeliveryState()) {
case "WA", "CA" -> 2;
case "OR", "TX", "NY" -> 3;
default -> 4;
};
return order.getPlacedOn().plusDays(deliveryTime);
}
}
After
public static LocalDate regularDeliveryDate(Order order) {
int deliveryTime = switch (order.getDeliveryState()) {
case "WA", "CA" -> 2;
case "OR", "TX", "NY" -> 3;
default -> 4;
};
return order.getPlacedOn().plusDays(deliveryTime);
}
public static LocalDate rushDeliveryDate(Order order) {
int deliveryTime = switch (order.getDeliveryState()) {
case "WA", "CA", "OR" -> 1;
case "TX", "NY", "FL" -> 2;
default -> 3;
};
return order.getPlacedOn().plusDays(deliveryTime);
}
Refactoring 3. 여러 함수를 클래스로 묶기
- 비슷한 매개변수 목록을 여러 함수에서 사용한다면, 해당 메서드를 모아 클래스를 만들 수 있다.
- 클래스 내부로 메서드를 옮기고 데이터를 필드로 만들면 메서드에 전달해야 하는 매개변수의 수를 줄일 수 있다.
Before
public static void main(String[] args) {
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);
});
}
}
...
After
public static void main(String[] args) {
new StudyPrinter(this.totalNumberOfEvents, participants).print(participants);
}
public class StudyPrinter {
private int totalNumberOfEvents;
private List<Participant> participants;
public StudyPrinter(int totalNumberOfEvents, List<Participant> participants) {
this.totalNumberOfEvents = totalNumberOfEvents;
this.participants = participants;
}
public void print(List<Participant> participants) throws IOException {
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);
});
}
}
private String header(int totalNumberOfParticipants) {
StringBuilder header = new StringBuilder(String.format("| 참여자 (%d) |", totalNumberOfParticipants));
for (int index = 1; index <= this.totalNumberOfEvents; index++) {
header.append(String.format(" %d주차 |", index));
}
header.append(" 참석율 |\n");
header.append("| --- ".repeat(Math.max(0, this.totalNumberOfEvents + 2)));
header.append("|\n");
return header.toString();
}
private String getMarkdownForParticipant(String username, Map<Integer, Boolean> homework) {
return String.format("| %s %s | %.2f%% |\n", username, checkMark(homework), getRate(homework));
}
private String checkMark(Map<Integer, Boolean> homework) {
StringBuilder line = new StringBuilder();
for (int i = 1 ; i <= this.totalNumberOfEvents ; i++) {
if(homework.containsKey(i) && homework.get(i)) {
line.append("|:white_check_mark:");
} else {
line.append("|:x:");
}
}
return line.toString();
}
private double getRate(Map<Integer, Boolean> homework) {
long count = homework.values().stream()
.filter(v -> v == true)
.count();
return (double) (count * 100 / this.totalNumberOfEvents);
}
}
'클린코드' 카테고리의 다른 글
[Refactoring] 6. 가변 데이터 (0) | 2022.09.09 |
---|---|
[Refactoring] 5. 전역 데이터 (0) | 2022.09.08 |
[Refactoring] 3. 긴 함수 (0) | 2022.09.06 |
[Refactoring] 2. 중복 코드 (0) | 2022.09.05 |
[Refactoring] 1. 이해하기 힘든 이름 (0) | 2022.09.05 |