리팩토링 14. 매개변수를 질의 함수로 바꾸기
- etc/리팩토링
- 2023. 4. 30.
들어가기 전
이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다.
리팩토링 14. (Replace Parameter With Query)
- 함수의 매개변수 목록은 함수의 다양성을 대변하며, 매개변수가 적을수록 이해하기 쉽다.
- 한 매개변수를 통해 다른 매개변수를 알아낼 수 있다면 '중복 매개변수'라 생각할 수 있다.
- 매개변수에 값을 전달하는 것은 '함수를 호출하는 쪽'의 책임이다.
- 너무 많은 매개변수를 전달해야 한다면, 클라이언트에서 너무 많은 책임을 가지고 있음을 의미한다. 클라이언트가 사용하기도 어렵다.
- 가능하면 함수를 호출하는 쪽의 책임을 줄이고, 함수 내부에서 책임지도록 노력한다.
- '임시 변수를 질의 함수로 바꾸기'와 '함수 선언 변경하기'를 통해 이 리팩토링을 적용한다.
- 매개변수를 줄이면서 새로운 의존성이 발생할 수 있다. 이 경우, 매개변수를 줄이는게 맞는지 고민해봐야 한다.
매개변수의 의미
전달되는 매개변수의 목록은 함수를 이해하는데 중요한 역할을 한다. 함수에 전달되는 매개변수 목록이 다르면, 함수가 하는 일도 다르겠다라는 것이 자연스러운 생각이다. 함수에 전달되는 매개변수의 목록이 많으면 많을수록, 함수가 하는 일이 무엇인지 추측하기가 어려워진다. 따라서 가능하면 함수가 전달받는 매개변수의 갯수를 줄이는 것이 좋다.
중복 매개변수란?
전달된 매개변수를 통해 다른 매개변수를 얻어낼 수 있다면, 그 매개변수는 중복된 매개변수다. 이런 중복된 매개변수는 '쿼리 메서드'를 통해서 메서드의 매개변수 목록에서 제거할 수 있다. 메서드의 매개변수를 만들 때, 함수를 사용하는 쪽을 고려해야 한다.
클라이언트를 생각하면, 매개변수는 적어야 한다.
함수를 사용하는 쪽은 매개변수 값들을 결정해서 넘겨줘야한다. 그런데 매개변수가 너무 많다면, 함수를 호출하는 쪽에서 많은 책임을 가지고 있고, 심지어 사용하기가 어려워진다. 그래서 매개변수 갯수를 줄이고, 함수 내부에서 책임을 가져간다면 함수를 사용하는 쪽에서 좀 더 편리하게 함수를 사용할 수 있다.
그렇지만 매개변수를 줄이는 것만이 좋은 선택은 아니다. 매개변수를 줄이면서 오히려 새로운 의존성이 생긴다면, '이 의존성이 생겨도 맞는 것인지'를 고민해야한다. 만약 이 의존성이 생기면 안된다면, 매개변수를 그대로 두는 것이 맞는 방향이 될 것이다.
Before
아래 코드를 살펴보자. 현재 수정해야 할 부분은 두 가지가 존재한다.
- discountLevel을 구하는 공식이 구현 세부 사항이다. 메서드로 추출해서 알기 쉽게 바꾸어야 한다.
- 매개변수가 2개다. 1개로 줄일 수 있을 것 같다.
discountLevel을 계산하는 것을 getDiscountLevel() 메서드로 추출했다. 이렇게 하니 getDiscountLevel()은 멤버 변수가 되어서 어디에서도 호출할 수 있게 된다. 즉, discountedPrice() 메서드를 호출할 때 discountLevel을 직접 전달하지 않아도 된다는 것이다
discountedPrice() 메서드에서 discountLevel을 제거하고, discountedPrice 내부에서 쿼리 메서드인 getDiscountLevel()을 호출해서 직접 가져와서 작업을 하면 될 것 같다.
이렇게 하면 다음 결과가 기대된다.
discountedPrice()는 매개변수를 1개만 요구한다. 따라서 이 메서드를 사용하는 클라이언트의 책임이 줄어들었고 (discountLevel을 계산해야 할), 이 책임은 discountedPrice()가 가져가게 되었다.
public class Order {
private int quantity;
private double itemPrice;
public Order(int quantity, double itemPrice) {
this.quantity = quantity;
this.itemPrice = itemPrice;
}
public double finalPrice() {
double basePrice = this.quantity * this.itemPrice;
// 이 수식은 이해하기 어려움. 의미를 가진 메서드로 빼야할 듯
int discountLevel = this.quantity > 100 ? 2 : 1;
return this.discountedPrice(basePrice, discountLevel);
}
// basePrice, discountLevel이 있음.
private double discountedPrice(double basePrice, int discountLevel) {
return discountLevel == 2 ? basePrice * 0.90 : basePrice * 0.95;
}
}
After
위의 내용을 감안해서 코드를 리팩토링하면 아래와 같이 변경된다. 코드에서 확인할 수 있듯이, discountedPrice()는 이제 1개의 메서드만 요구한다.
public class Order {
private int quantity;
private double itemPrice;
public Order(int quantity, double itemPrice) {
this.quantity = quantity;
this.itemPrice = itemPrice;
}
public double finalPrice() {
double basePrice = this.quantity * this.itemPrice;
return this.discountedPrice(basePrice);
}
private int getDiscountLevel() {
return this.quantity > 100 ? 2 : 1;
}
// basePrice, discountLevel이 있음.
private double discountedPrice(double basePrice) {
return getDiscountLevel() == 2 ? basePrice * 0.90 : basePrice * 0.95;
}
}
'etc > 리팩토링' 카테고리의 다른 글
리팩토링 16. 여러 함수를 클래스로 묶기 (0) | 2023.04.30 |
---|---|
리팩토링 15. 플래그 인수 제거하기 (0) | 2023.04.30 |
냄새 4. 긴 매개변수 목록 (0) | 2023.04.30 |
리팩토링 13. 조건문을 다형성으로 바꾸기 (0) | 2023.04.29 |
리팩토링 12. 반복문 쪼개기 (0) | 2023.04.29 |