1. 알고자 하는 것
회사에서 코드를 작성할 때, Flag 변수를 생각보다 많이 사용했다는 것을 이번 강의를 보고 느끼게 되었다.
단순히 private 메서드 개수를 줄이기 위해 / 공통 메서드로 묶기 위해 Flag 변수를 두어 메서드 하나에서 로직을 분기했었다.
내 딴에서는 공통로직의 재사용이라는 리팩토링 명목으로 사용한 Flag 변수였지만, 이번 강의를 통해 '단순히 라인 수를 줄이는 것이 리팩토링이 아니다' 라는 의미를 한번 더 깨닫게 된 것 같다.
리팩토링은 '짧은 코드를 만드는 것'이 목적이 아니라 '의도를 명확히 / 가독성을 좋게' 하는 것이 목적임을 늘 상기하자!
- 플래그 인수 제거하기
2. 알게된 것
- boolean, enum 타입과 같은 Flag 변수를 통해 함수 내부의 로직을 분기하는 경우가 많다.
- 이 때, Flag 변수를 통해 함수 내부의 로직을 분기하게 되었을 때, 호출하는 쪽에서는 해당 메서드의 내부 로직을 보기 전까지는 함수의 의도를 파악하기 어렵다.
public static void main(String[] args) {
Order order = new Order(LocalDate.now(), "WA");
Shipment shipment = new Shipment();
// Flag 변수가 true일 때 무슨 동작을 하는지?
LocalDate rushDeliveryDate = shipment.deliveryDate(order, true);
// false일 때는?
LocalDate regularDeliveryDate = shipment.deliveryDate(order, false);
}
// 해당 메서드까지 찾아가서 로직을 확인해야 명시적으로 파악할 수 있다
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);
}
}
- 이렇게 Flag 변수를 매개변수로 넘겨주기보다는 조건문 분해하기를 활용해 함수 내부의 분기 로직을 각 메서드로 추출하고, 호출하는 쪽에서 조건에 따라 명시적인 이름을 갖는 각 메서드를 호출함으로써 각 조건에 따른 의도를 명확하게 파악할 수 있도록 한다.
public static void main(String[] args) {
Order order = new Order(LocalDate.now(), "WA");
Shipment shipment = new Shipment();
// isRush가 true일 때는 빠른 배송 날짜를 반환한다.
// isRush가 false일 때는 일반 배송 날짜를 반환한다.
LocalDate deliveryDate = order.getIsRush() ?
getRushDeliveryDate(order) :
getRegularDeliveryDate(order);
}
public static LocalDate getRegularDeliveryDate(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 getRushDeliveryDate(Order order) {
int deliveryTime = switch (order.getDeliveryState()) {
case "WA", "CA", "OR" -> 1;
case "TX", "NY", "FL" -> 2;
default -> 3;
};
return order.getPlacedOn().plusDays(deliveryTime);
}
- 이와 더불어, 메서드 역시 더 이상 기존 Flag 변수를 매개변수로 가지지 않으므로 매개변수의 개수도 줄일 수 있다.
3. 정리
- boolean, enum과 같은 Flag 변수를 메서드의 매개변수를 넘겨주어 메서드 내부 로직을 분기하는 경우가 많다.
- 이 때, 호출하는 쪽에서는 Flag 변수에 따라 메서드의 내부 동작이 어떻게 바뀌는지 파악하기 어렵다.
- 따라서 Flag 변수에 따라 분기되는 로직을 명시적인 이름을 갖는 각 메서드로 추출하고 (조건문 분해하기), 호출하는 쪽에서 분기하여 해당 메서드를 호출한다.
- 이를 통해 호출하는 쪽에서는 각 조건에 따라 명시적인 이름을 갖는 메서드를 호출함으로써 조건에 해당하는 동작을 예측하기 쉽고, 로직을 수행하는 메서드 쪽에서도 Flag 매개변수가 하나 줄어드는 장점을 가진다.
Reference
'Clean Code' 카테고리의 다른 글
Java 리팩토링 - 함수 옮기기 (Move Function) (0) | 2023.12.25 |
---|---|
Java 리팩토링 - 변수 캡슐화하기 (with. Getter/Setter) (0) | 2023.12.17 |
Java 리팩토링 - 조건문을 다형성으로 바꾸기 (0) | 2023.11.05 |
Java 리팩토링 - 조건문 분해하기 (0) | 2023.10.31 |
Java 리팩토링 - 함수를 명령(Command)으로 바꾸기 (0) | 2023.10.06 |