긴 매개변수 목록(Long Parameter List)

박상훈·2022년 8월 18일
0
어떤 함수에 매개변수가 많을수록 함수의 역할을 이해하기 힘들다
함수는 한가지 일을 하는지, 불필요한 매개변수는 없는지, 레코드로 뭉칠 수 있는지

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

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

discountedPrice 메서드의 매개변수 discountLevel 을 질의 함수로 변경하여
매개변수에서 제거하고 함수를 직접 호출하도록 변경

before

class Refactoring {
    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;
    }
}

after

class Refactoring {
    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.9 : basePrice * 0.95;
    }
}

플래그 인수 제거하기(Remove Flag Argument)

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

긴 함수 냄새에서 사용한 조건문 분해하기로 true/false 에 대한 작업을 분해하여 메서드를 생성
테스트 코드에서 매개변수를 전달하여 사용하던 방식에서 플래그를 지우고 분해한 메서드를 직접 호출

before

class Refactoring {
    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

class Refactoring {
    public 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 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);
    }   
}

여러 함수를 클래스로 묶기(Combine Functions into Class)

  • 비슷한 매개변수 목록을 여러 함수에서 사용하고 있다면 해당 메소드를 모아서 클래스를 만든다
  • 클래스 내부로 메소드를 옮기고 데이터를 필드로 만들면 메서드에 전달해야 하는 매개변수 목록을 줄일 수 있다

before 를 확인해보면 메서드에 동일하게 자주 사용되는 매개변수가 있다
새로운 클래스를 생성하여 해당 메소드를 모아서 필드로 처리하여 매개변수를 줄이고
after 클래스는 새로운 클래스의 메서드를 호출 한다

before

class Refactoring {
    private void print() throws IOException, InterruptedException { }

    private String getMarkdownForParticipant(String username, Map<Integer, Boolean> homework) { }

    private String checkMark(Map<Integer, Boolean> homework, int totalNumberOfParticipants) { }

    private double getRate(Map<Integer, Boolean> homework) { }

    private String header(int totalNumberOfParticipants) { }
}

after

class Refactoring {
    private void print() throws IOException, InterruptedException { 
        new newAfter(매개변수).print();  
    }

    private double getRate(Map<Integer, Boolean> homework) { }
  
    private String getMarkdownForParticipant(String username, Map<Integer, Boolean> homework) { }
}

class NewRefactoring {
    
    private int totalNumberOfParticipants;
    
    newAfter(int totalNumberOfParticipants) {
        this.totalNumberOfParticipants = totalNumberOfParticipants;
    }
    
    private void print() throws IOException, InterruptedException { }
  
    private String checkMark(Map<Integer, Boolean> homework) { }
  
    private String header() { }
}
profile
엔지니어

0개의 댓글