Language/Java

긴 매개변수 목록

JUNGKEUNG 2022. 8. 15. 18:35
  • 어떤 함수에 매개변수가 많을수록 함수의 역할을 이해하기 어려워진다
    • 그 함수는 한가지 일을 하고 있는게 맞는가?
    • 불필요한 매개변수는 없는가?
    • 하나의 레코드를 뭉칠 수 있는 매개변수 목록은 없는가?
  • 어떤 매개변수를 다른 매개변수를 통해 알아낼 수 있다면, “매개변수를 질의 함수로 바꾸기”를 사용할 수 있다
  • 기존 자료구조에서 세부적인 데이터를 가져와서 여러 매개변수로 넘기는 대신,”객체 통째로 넘기기”를 사용할 수 있다
  • 일부 매개변수들이 대부분 같이 넘겨진다면, “매개변수 객체 만들기”를 적용할 수 있다
  • 매개변수가 플래그로 사용된다면, “플래그 인수 제거하기”를 사용할 수 있다
  • 여러 함수가 일부 매개변수를 공통적으로 사용한다면 “여러 함수를 클래스 묶기”를 통해매개변수를 해당 클래스의 필드로 만들고 매서드에 전달해야 할 매개변수 목록을 줄일 수 있다.

 

매개변수를 질의 함수로 바꾸기

  • 함수의 매개변수 목록은 함수의 다영성을 대변하며, 짧을수록 이해하기 좋다
  • 어떤 한 매개변수를 다른 매개변수를 통해 알아낼 수 있다면 “중복 매개변수”라 생각할 수 있다.
  • 매개변수에 값을 전달하는 것은 “함수를 호출하는 쪽”의 책임이다. 가능하면 함수를 호출하는 쪽의 책임을 줄이고 함수 내부에서 책임지도록 노력한다
  • “임시 변수를 질의 함수로 바꾸기”와 “함수 선언 변경하기”를 통해 이 리팩토링을 적용한다.

리팩토링 전

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);
    }

    private double discountedPrice(double basePrice, int discountLevel) {
        return discountLevel == 2 ? basePrice * 0.9 : basePrice * 0.95;
    }
}
  • int discountLevel 매개변수는 중복 매개변수로 함수로 따로 빼내야한다.
  • 함수로 따로 빼서 그 함수가 책임을가지게 하고 가능하면 함수를 호출하는쪽이 책임을 줄이고 함수 내부에서 책임을 가지게 한다.

 

리팩토링 후

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 discountLevel() {
        return this.quantity > 100 ? 2 : 1;
    }

    private double discountedPrice(double basePrice) {
        return discountLevel() == 2 ? basePrice * 0.90 : basePrice * 0.95;
    }
}

 

 

플래그 인수제거

  • 플래그는 보통 함수에 매개변수로 전달해서, 함수 내부의 로직을 분기하는데 사용한다.
  • 플래그를 사용한 함수는 차이를 파악하기 어렵다
    • bookConcert ( customer, false ), bookConcert( customer, true)
    • bookConcert ( customer ), premuimBookConcert ( customer)
  • 조건문 분해하기 ( Decompose Condition ) 를 활용할 수 있다.

매개변수를 플래그라고 불린다.

 

 

리팩토링 전

public class Shipment {

    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);
        }
    }
}

class ShipmentTest {

    @Test
    void deliveryDate() {
        LocalDate placedOn = LocalDate.of(2021, 12, 15);
        Order orderFromWA = new Order(placedOn, "WA");

        Shipment shipment = new Shipment();
assertEquals(placedOn.plusDays(1), shipment.deliveryDate(orderFromWA, true));
assertEquals(placedOn.plusDays(2), shipment.deliveryDate(orderFromWA, false));
    }

}
int deliveryTime = switch (order.getDeliveryState()) {
                case "WA", "CA", "OR" -> 1;
                case "TX", "NY", "FL" -> 2;
                default -> 3;
  • 빠른 배송을 선택 했을때 WA, CA, OR 하루 , TX NY FL 2일 , 나머지는 3일
  • 그냥 배송을 선택 했을떄 WA,CA 2일 OR, TX, NY 3일 나머지 4일
  • 각각의 조건의 액션들을 메서드로 분리하자

 

리팩토링 후

public class Shipment {

    private 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);
    }

    private 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);
    }
}
class ShipmentTest {

    @Test
    void deliveryDate() {
        LocalDate placedOn = LocalDate.of(2021, 12, 15);
        Order orderFromWA = new Order(placedOn, "WA");

        Shipment shipment = new Shipment();
assertEquals(placedOn.plusDays(1), shipment.rushDeliveryDate (orderFromWA));
assertEquals(placedOn.plusDays(2), shipment.regularDeliveryDate (orderFromWA));
    }

}

리팩토링 전보다 명시적으로 코드가 잘 보이고 어떤기능을 하고 무엇을 위해 만든 코드인지 알기 쉬워졌다.

'Language > Java' 카테고리의 다른 글

Eclipse에 Azure 설치 하기  (0) 2022.10.27
엔티티의 생명주기  (0) 2022.10.10
긴 함수(2)  (0) 2022.08.14
[리팩토링] 긴 함수  (0) 2022.08.06
Optional  (0) 2022.07.02