리팩토링

메서드정리

  • 리팩토링의 주된 작업은 코드를 포장하는 메서드를 적절히 정리하는 것.

장황한 메서드에 많은 정보가 들어가는데 마구 얽힌 복잡한 로직들에 정보가 묻힌다.핵심적인 리펙토링 기법은 메서드 추출과 메서드 삽입이 있다.

메서드 추출에서 가장 어려운 작업은 지역변수를 처리하는 것인데 그것은 주로 임시 변수 때문이다.

임시변수를 메서드 호출로 전환을 실시해서 없어도 되는 임시변수는 전부 제거

메서드 추출 (extract Method)

  • 어떤코드를 그룹으로 묶어도 되겠다고 판단될 땐 그 코드를 빼내어 목적을 잘 나타내는 직관적 이름의 메서드로 만들자.

void printOwing(double mount){
  printBanana();

  System.out.println("name " + _name);
  System.out.println("mount " + mount);
}

리팩토링

void printOwing(double mount){
  printBanana();
  printDetail(mount);
}

private void printDetail(double mount) {
  System.out.println("name " + _name);
  System.out.println("mount " + mount);
}


  • 동기

    • 메서드 코드가 너무길거나 코드에 주석을 달아야 의도를 이해할 수 있을 때 사용함

    • 직관적 이름의 간결한 메서드가 좋은 이유   
      1.메서드가 잘게 쪼개져 있을 경우 다른메서드에서 사용하기 쉽다.
      2.상위 계층의 메서드에서 주석같은 더 많은 정보를 읽을수 있다
      3.재정의 하기 훨씬 수월하다.


  • 방법

    • 목적에 부합하는 이름의 새 메서드를 생성하자. 이때 메서드명은 원리가 아니라
      기능을 나타내는 이름으로 해야좋다.

    • 기존의 메서드에서 빼낸 코드를 새로 생성한 메서드로 복사하자.

    • 빼낸 코드에서 기존 메서드의 모든 지역변수 참조를 찾자 그것들을 새로 생성한 메서드의 지역변수나 매개변수로 사용

    • 빼낸 코드에서 읽어들인 지역변수를 대상 메서드에 매개변수로 전달한다.

    • 모든 지역변수 처리를 완료했으면 컴파일을 실시하자.

  


  • 예제 : 지역변수 사용안함

void printOwing(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;

  System.out.println("----------------------");
  System.out.println("---------고객외상-------");
  System.out.println("----------------------");
 
  while(e.hasMoreElements()){
      Order each = (Order) e.nextElement();
      outstanding += each.getAmout();
  }

  System.out.println("고객명 : " + _name);
  System.out.println("외상액 : " + outstanding);
 
}


    • 위의 코드중 배너 관련 코드를  아래와 같이 리팩토링 할 수 있다


void printOwing(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;

  printBaner();

  while(e.hasMoreElements()){
      Order each = (Order) e.nextElement();
      outstanding += each.getAmout();
  }

  System.out.println("고객명 : " + _name);
  System.out.println("외상액 : " + outstanding);

}

private void printBaner() {
  System.out.println("----------------------");
  System.out.println("---------고객외상-------");
  System.out.println("----------------------");
}

  • 예제 : 지역변수 사용

void printOwing(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;

  printBaner();

  while(e.hasMoreElements()){
      Order each = (Order) e.nextElement();
      outstanding += each.getAmout();
  }

  System.out.println("고객명 : " + _name);
  System.out.println("외상액 : " + outstanding);

}


    • 위 코드 중 세부내역 출력코드를 하나의 매개변수를 받는 메서드로 리팩토링 가능.


void printOwing(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;

  printBaner();

  while(e.hasMoreElements()){
      Order each = (Order) e.nextElement();
      outstanding += each.getAmout();
  }

  printDetails(outstanding);

}

private void printDetails(double outstanding) {
  System.out.println("고객명 : " + _name);
  System.out.println("외상액 : " + outstanding);
}


  • 예제: 지역변수를 다시 대입하기

void printOwing(){
  Enumeration e = _orders.elements();
  double outstanding = 0.0;
  printBaner();
  while(e.hasMoreElements()){
      Order each = (Order) e.nextElement();
      outstanding += each.getAmout();
  }
  printDetails(outstanding);
}

    • 계산부분을 아래와 같이 리팩토링 할 수 있음.

void printOwing(){
  printBaner();
  double outstanding = getOutstanding();
  printDetails(outstanding);
}
private double getOutstanding() {
  Enumeration e = _orders.elements();
  double outstanding = 0.0;
  while(e.hasMoreElements()){
      Order each = (Order) e.nextElement();
      outstanding += each.getAmout();
  } return outstanding;
}

    • 열거형 e 변수는 빼낸 코드에서만 사용되므로 새메서드로 아예 옮겨도 된다.
      메서드를 컴파일하고 테스트 했으면 언제나 처럼 반환값의 이름을 변경하자..


void printOwing(){
  printBaner();
  double result = getOutstanding();
  printDetails(outstanding);
}
private double getOutstanding() {
  Enumeration e = _orders.elements();
  double outstanding = 0.0;
  while(e.hasMoreElements()){
      Order each = (Order) e.nextElement();
      result += each.getAmout();
  } return result;
}

    • 변수를 두개 이상 반환해야 할때는 변수를 반환하는 각각의 메서드를 2개 만드는게
      최선의 방법이다.


메서드 내용 직접 삽입 (inline Method)

  • 메서드 기능이 너무 단순해서 메서드명만 봐도 너무 뻔할 땐 그 메서드의 기능을 호출하는 메서드에 넣고 그 메서드는 삭제하자

int getRating(){
  return (moreThanFiveLateDeliveries()) ?2:1;
}

private boolean moreThanFiveLateDeliveries() {
return _numberOfLateDeliveries > 5;
}

위의 코드를 아래와 같이 리팩토링 가능하다

int getRating(){
  return (_numberOfLateDeliveries > 5) ?2:1;
}


  • 동기

    • 메서드 기능이 지나치게 단순할 때는 메서드를 없애야 한다. 불필요한 인다이렉션은 장애물일 뿐이다.


  • 방법

    • 메서드가 재정의되어 있지 않은지 확인하자.

    • 메서드를 호출하는 부분을 모두 찾자.

    • 호출부분을 메서드 내용으로 교체하자.

    • 테스트를 하자.

    • 메서드 정의를 삭제하자.


임시변수 내용 직접 삽입 (inline Temp)

  • 간단한 수식을 대입받는 임시변수로 인해 다른 리팩토링 기법 적용이 힘들땐 그 임시변수를 참조하는 부분을 전부 수식으로 치환하자.

double bestPrice = anOrder.bestPrice();
return (bestPrice > 1000);

위의 코드를 아래와 같이 리팩토링 가능하다

return (anOrder.bestPrice() > 1000);


  • 동기

    • 임시변수가 메서드 추출등 다른 리팩토링에 방해가 된다면 임시변수 내용 직접삽입을
      적용해야함


  • 방법

    • 대입문의 우변에 문제가 없는지 확인

    • 문제가 없다면 임시변수를 final로 선언하고 컴파일하자.

    • 수정을 마칠때마다 컴파일과 테스트 하자.


임시변수 메서드 호출로 전환 (repalce Temp with Query)

  • 수식의 결과를 저장하는 임시변수가 있을 땐 그 수식을 빼내어 메서드로 만든 후 임시변수 참조부분을 전부 수식으로 교체  (다른메소드에서 호출가능)

double basePrice = _quantitiy * _itemPrice;
if (basePrice > 1000)
  return basePrice * 0.95;
else
  return basePrice * 0.98;

위의 코드를 아래와 같이 리팩토링 가능하다

public double test() {
 if (basePrice() > 1000)
      return basePrice() * 0.95;
   else
      return basePrice() * 0.98;
}
private double basePrice() {
  return _quantitiy * _itemPrice;
}


  • 동기

    • 임시변수는 일시적이고 적용이 국소적 범위로 제한된다는 단점이 있음 임시변수는 자신이 속한 메서드 안에서만 인식되므로 외부에서 접근하려면 코드가 길어짐 그래서 메서드호출로 전환하면 코드가 훨씬 깔끔해짐


  • 방법

    • 값이 한번만 대입되는 임시변수를 찾자.

    • 그 임시 변수를 final로 선언하자

    • 컴파일을 실시하자.

    • 대입문 우변을 빼내어 메서드로 만들자.

    • 컴파일과 테스트를 실시하자.

  • 예제

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

    • 아래와 같이 리팩토링 가능하다 임시변수를 메서드로 추출함으로써 직관적이고 깔끔한 코드가 완성됐다.


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

private int basePrice() {
  return _quantitiy * _itemPrice;
}

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



직관적 임시변수 사용 (introduce Explaining Variable)

  • 사용된 수식이 복잡할 땐 수식의 결과나 수시의 일부분을 용도에 부합하는 직관적 이름의 임시변수에 대입하자.

if((platform.toUpperCase().indexOf("MAC") > -1)&&
      (browser.toUpperCase().indexOf("IE") > -1)){
  //기능코드
}

위의 코드를 아래와 같이 리팩토링 가능하다

final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIeBrowser = browser.toUpperCase().indexOf("IE") > -1;


if(isMacOs && isIeBrowser){
  //기능코드
}


  • 동기

    • 수식이 너무 복잡해져서 이애하기 힘들 경우 임시변수를 사용하면 수식을 더 처리하기 쉽게 쪼갤수 있다.

  • 방법

    • 수식에서 한부분의 결과를 임시변수의 값으로 교체하자

    • 그 임시 변수를 final로 선언하자

    • 컴파일과 테스트를 실시하자.








+ Recent posts