본문 바로가기

프로그램언어/Refactoring

[ Refactoring ] Replace Temp with Query Method

어떤 수식의 결과값을 저장하기 위해서 임시변수를 사용하고 있다면, 수식을 뽑아내서 메소드로 만들고, 임시변수를 참조하는 곳을 찾아 모두 메소드 호출로 바꾼다. 새로 만든 메소드는 다른 메소드에서도 사용될 수 있다.

  double basePrice = _quantity * _itemPrice;
     if (basePrice > 1000)
         return basePrice * 0.95;
     else
         return basePrice * 0.98;
  if (basePrice() > 1000)
         return basePrice() * 0.95;
     else
         return basePrice() * 0.98;
...
   double basePrice() {
       return _quantity * _itemPrice;
   }


동기

  • 임시변수는 그것이 사용되는 메소드의 컨텍스트 안에서만 볼 수 있으므로, 임시변수가 사용되는 메소드는 보통 길이가 길어지는 경향이 있다.
  • 임시변수를 query method 로 바꿈으로써 클래스 내의 어떤 메소드도 임시변수에 사용될 정보를 얻을 수 있다. 클래스 코드가 더 깔끔해진다.
  • Extract Method 적용하기 전의 필수 단계.
  • 지역변수는 메소드의 추출을 어렵게 하기 때문에 가능한 많은 지역변수를 query method 로 바꾸는것이 좋다.

절차

  • 간단한 경우(임시변수에 값이 한번만 대입되고, 대입문(assignment)을 만드는 수식이 부작용을 초래하지 않는 경우)에 대한 절차는 다음과 같다.
    • 임시 변수에 값이 한번만 대입되는지를 확인한다.
    • 임시변수를 final 로 선언한다.
    • 컴파일 한다.
    • 대입분의 우변을 메소드로 추출한다.
    • 컴파일과 테스트를 한다.
    • Inline Temp를 적용한다.

예제

double getPrice() {
        int basePrice = _quantity * _itemPrice;
                
        double discountFactor;
        if (basePrice > 1000) discountFactor = 0.95;
        else discountFactor = 0.98;
        return basePrice * discountFactor;
    }

두개의 임시변수(basePrice,discountFactor)를 모두 제거하고 싶다.

임시변수에 값이 한번만 대입되는 것을 확인하기 위해(문제가 있으면 이 리팩토링을 하면 안된다) final 로 선언하여 테스트한다.


double getPrice() {
       final int basePrice = _quantity * _itemPrice;
       final double discountFactor;
        if (basePrice > 1000) discountFactor = 0.95;
        else discountFactor = 0.98;
        return basePrice * discountFactor;
    }

대입문의 우변을 메소드로 뽑아낸다.


double getPrice() {    
        final int basePrice = basePrice(); final double discountFactor; if (basePrice > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; } private int basePrice() { return _quantity * _itemPrice; }

컴파일,테스트후 Inline Temp 를 사용한다.


 double getPrice() {
        final int basePrice = basePrice();
        final double discountFactor; 
       if (basePrice() > 1000) discountFactor = 0.95; else discountFactor = 0.98; return basePrice * discountFactor; }

컴파일, 테스트후 두번째 참조하는 곳도 바꾸고 basePrice 변수도 삭제한다.(더이상 참조하는 곳이 없으므로)


double getPrice() {
        final double discountFactor;
        if (basePrice() > 1000) discountFactor = 0.95;
        else discountFactor = 0.98;
    return basePrice() * discountFactor;    
}

discountFactor에 대해서도 위와같이 작업한다.


double getPrice() {
    final double discountFactor = discountFactor();        
        return basePrice() * discountFactor;
    }

    private double discountFactor() {
        if (basePrice() > 1000) return 0.95;
        else return 0.98;
    }

최종 결과

double getPrice() {
        return basePrice() * discountFactor();
    }

 basePrice() 가 총 두번 호출되므로 이전보다 한번의 계산이 더 늘었다.
  • 퍼포먼스에 대해서 걱정이 될 수 있다.
  • 당장은 약간 느려질지도 모르나 십중팔구는 문제가 되지 않을것이다.
  • 만약 문제가 된다면 최적화 단계에서 수정하면 된다.
  • 코드가 잘 분해되어 잇으면 보통 리팩토링을 하지 않았을 때에는 보지 못했을 더 강력한 최적화 방법을 찾을 수 있다.
  • 최악의 경우라도, 임시변수를 다시 집어넣는 것은 쉽다.