Clean Code

Java 리팩토링 - 함수 인라인, 클래스 인라인 (Function Inline, Class Inline)

 

1. 알고자 하는 것

이전에 함수 추출하기(Extract Method) 글을 통해 코드의 내용에 직관적인 이름을 붙여 가독성을 높일 수 있다고 학습한 적이 있었다.

이러한 추출하기 기법은 함수 뿐 아니라 클래스 단위로도 가능하며, 마찬가지로 클래스로 코드의 내용을 추출해 직관적으로 코드의 가독성을 높이고 응집도를 높일 수 있다.

 

하지만 함수/클래스 추출하기와 반대로, 때로는 함수/클래스로 추출했던 코드를 다시 본문 코드에 옮겨넣는 것이 코드의 가독성을 높일 수 있는 상황이 있다. 이러한 리팩토링 방법을 함수/클래스 인라인 (Function / Class Inline) 이라고 한다.

 

  • 함수 인라인 (Function Inline)
  • 클래스 인라인 (Class Inline)

 

 

2. 알게 된 것

 

  • 함수/클래스 추출하기를 사용해 코드의 가독성을 높인 뒤에, 기능의 변경사항으로 코드가 줄어들거나 변경되고 나서 다시 보았을 때 다음과 같은 형태의 코드가 되어있을 수도 있다.
    • 기존에 추출했던 코드가 간소화되어 이제는 함수/클래스의 본문이 함수/클래스의 이름만큼, 또는 함수/클래스의 이름보다 의도를 더 명확하게 표현하게 됨
    • 단순히 함수/클래스의 호출을 감싸는 우회형 (Indirection) 함수/클래스가 됨
    • 함수/클래스 추출이 잘못되어 다시 하나로 합친 뒤 리팩토링을 해야함
  • 이런 경우, 추출한 함수/클래스를 다시 합치는 Inline 리팩토링 기법을 사용할 수 있다.

 

함수 인라인 (Function Inline)

  • 코드의 변경으로 추출했던 함수가 다음과 같이 한 줄의 코드만을 포함하는 메서드가 되었다고 가정하자.
public class Rating {

    public int rating(Driver driver) {
        return moreThanFiveLateDeliveries(driver) ? 2 : 1;
    }

    // 함수 추출하기 (Extract Function)
    private boolean moreThanFiveLateDeliveries(Driver driver) {
        return driver.getNumberOfLateDeliveries() > 5;
    }
}
  • driver의 Late Delivery 수가 5 이상인지를 판단하는 메서드로, moreThanFiveLateDeliveries 라는 의도를 표현하는 메서드명으로 추출된 것은 분명 함수 추출하기의 리팩토링 의도로써 괜찮을 수 있다.
  • 하지만 본문 내용을 보았을 때, driver.getNumberOfLateDeliveries() > 5; 라는 코드를 보았을 때에도 충분히 코드의 의도를 파악할 수 있다.
  • 이런 경우 굳이 함수로 한줄의 코드를 감싸는 것 보다는 다시 함수를 본문에 inline 하여 메서드를 없애는 경우가 더 나을 수도 있다.
public class Rating {

    public int rating(Driver driver) {
        // Function inline
        return driver.getNumberOfLateDeliveries() > 5 ? 2 : 1;
    }
}

 

 

클래스 인라인 (Function Inline)

  • 다음과 같이 Shipment 클래스에서 클래스 추출하기를 통해 추출된 TrackingInformation 클래스를 가지고 있고, getTrackingInfo() 함수를 통해 trackingInformation의 정보를 출력하는 함수가 있다고 가정하자.
public class Shipment {

    /**
     * private String shippingCompany;
     * private String trackingNumber;
     */
    private TrackingInformation trackingInformation;

    public Shipment(TrackingInformation trackingInformation) {
        this.trackingInformation = trackingInformation;
    }

    public TrackingInformation getTrackingInformation() {
        return trackingInformation;
    }

    public void setTrackingInformation(TrackingInformation trackingInformation) {
        this.trackingInformation = trackingInformation;
    }

    public String getTrackingInfo() {
        // return this.shippingCompany + ": " + this.trackingNumber;
        // Output : SHP:135
        return this.trackingInformation.display();
    }
}
  • 이 때, getTrackingInfo() 함수는 단순히 TrackingInformation 클래스의 display() 함수의 호출을 감싸는 함수로써만 동작한다.
  • 이 경우, 단순히 delegate 역할만을 하는 TrackingInformation은 크게 의미를 가지지 않게 되므로 해당 클래스를 제거하고 해당 클래스가 가지는 필드와 display() 함수를 Shipment 클래스에 inline 시킬 수 있다.
public class Shipment {

    public static void main(String[] args) {
        // 기존 함수 호출
        // Shipment shipment = new Shipment(new TrackingInformation("UPS", "12345"));
        // shipment.getTrackingInfo();

        // 클래스 인라인 함수 호출
        Shipment shipment = new Shipment("UPS", "12345");
        shipment.getTrackingInfo();
    }

    // TrackingInformation Field 가져오기
    private String shippingCompany;
    private String trackingNumber;

    public Shipment(String shippingCompany, String trackingNumber) {
        this.shippingCompany = shippingCompany;
        this.trackingNumber = trackingNumber;
    }

    public String getShippingCompany() {
        return shippingCompany;
    }

    public String getTrackingNumber() {
        return trackingNumber;
    }

    public String getTrackingInfo() {
        // 기존 TrackingInformation의 display() 함수 inline
        return this.shippingCompany + ":" + this.trackingNumber;
    }
}
  • 단순히 delegate 역할만을 수행하던 TrackingInformation 클래스의 필드와 display() 함수를 Shipment에 inline 함으로써 불필요한 클래스와 delegate 함수를 줄일 수 있다. 

 

3. 정리

  • 함수/클래스 추출하기를 통해 코드의 내용에 대해 명확한 이름으로 의도를 표현할 수 있다.
  • 하지만 변경사항으로 코드가 변경됨으로써 기존 추출하기를 사용한 코드가 단순히 다음과 같은 역할이 될 수도 있다.
    • 기존 코드가 간소화되어 본문 자체가 함수명보다 의도를 더 잘 표현함
    • 단순히 함수/클래스를 감싸는 우회형 (Indirection) 코드가 됨
  • 이럴 때, 추출했던 함수/클래스를 기존 코드에 합치는 Inline 리팩토링 기법을 사용할 수 있다.
  • 이제는 불필요해진 함수를 줄일 수 있고, 단순히 delegate만을 수행하는 불필요한 클래스를 줄일 수 있다.

 


Reference

강의 - 코딩으로 학습하는 리팩토링 (백기선 강사님)