프로그램언어/Refactoring

[ Refactoring ] Replace Temp with Query Method

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

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