이 글은 리팩터링 2판(마틴 파울러) 책을 참고하였습니다.

순서

I. 배경
II. 절차
III. 예시
IV. 기타


I. 배경

여러 매개변수를 동시에 활용 해야 할 때 통째로 넘기고 꺼내쓰도록 한다.
이처럼 통째로 넘기는 변화에 대응하기 쉽고, 해당 함수가 더 다양한 데이터를 사용 해야 한다고 해도
매개변수 목록은 수정할 필요가 없다.

하지만 함수가 이 데이터모듈에 의존하기 원치않는다면 이 리팩토링은 수행하지 않는다.
특히, 데이터모듈과 함수가 각각 다른 클래스에 속해 있다면 더 그러하다.

 


II. 절차

1. 매개변수들을 원하는 형태로 받는 빈 함수를 만든다

마지막 단계에서 이 함수의 이름을 다시 변경할 것임.

2. 새 함수의 본문에서는 원래 함수를 호출하고, 새 매개변수와 원래 함수의 매개변수를 매핑한다.

3. 정적검사하고, 모든 호출자가 새 함수를 사용하도록 수정하며 점진적 리팩토링

4. 원래 함수를 인라인하기 (원래함수 지우고 하나하나 바꾸기)

5. 새 함수의 이름을 바꾸고 모든 호출자에 반영하기

 


III. 예시

코드 원본 (TEST코드가 메인 동작코드라고 생각)

#include "gtest/gtest.h"

class TemperatureRange {
 public:
  int low;
  int high;
};

TemperatureRange heating_plan_temperature_range = {26, 36};

int WithinRange(int bottom, int top) {
  return (bottom >= heating_plan_temperature_range.low)
      && (top <= heating_plan_temperature_range.high);
}

// 이 TEST코드를 동작코드라고 생각하자
TEST(PreserveWholeObject, WithinRangeFalse) {
  TemperatureRange room_days_temperature_range = {25, 27};
  int low = room_days_temperature_range.low;
  int high = room_days_temperature_range.high;
  if (!WithinRange(low, high)) {
    printf("room temperature went outside range\n");
  }
  ASSERT_FALSE(WithinRange(low, high));
}

TEST(PreserveWholeObject, WithinRangeTrue) {
  TemperatureRange room_days_temperature_range = {26, 27};
  int low = room_days_temperature_range.low;
  int high = room_days_temperature_range.high;
  if (!WithinRange(low, high)) {
    printf("room temperature went outside range\n");
  }
  ASSERT_TRUE(WithinRange(low, high));
}

 


1. 새로운 함수 "New_WithinRange" 만들기

// 일단 테스트코드 생략
#include "gtest/gtest.h"

class TemperatureRange {
 public:
  int low;
  int high;
};

TemperatureRange heating_plan_temperature_range = {26, 36};

int New_WithinRange(int bottom, int top) {
  /* 새 함수에서는 원래 함수 호출*/
  WithinRange(bottom, top);
}

int WithinRange(int bottom, int top) {
  return (bottom >= heating_plan_temperature_range.low)
      && (top <= heating_plan_temperature_range.high);
}

 


2. 원래 함수를 지우기 위한 작업 + 객체로 넘기기

// 일단 테스트코드 생략
#include "gtest/gtest.h"

class TemperatureRange {
 public:
  int low;
  int high;
};

TemperatureRange heating_plan_temperature_range = {26, 36};

int WithinRange(int bottom, int top) { /* 2. 이제 지워도 된다 */
  return (bottom >= heating_plan_temperature_range.low)
      && (top <= heating_plan_temperature_range.high);
}

int New_WithinRange(TemperatureRange* tempRange) {  /* 객체로 받는다 */
  /* 새 함수에서는 원래 함수 호출*/
  WithinRange(tempRange->low, tempRange->high); /* 1. 매개변수 수정 */
  
  /* 2. 사실상 기존 함수와 위 함수호출 지우고 구현을 옮겨올 수 있음 */
  return ((temperature_range->low >= heating_plan_temperature_range.low)
      && (temperature_range->high <= heating_plan_temperature_range.high));
}

 


3. 최종 코드와 변경된 테스트코드

// 일단 테스트코드 생략
#include "gtest/gtest.h"

class TemperatureRange {
 public:
  int low;
  int high;
};

TemperatureRange heating_plan_temperature_range = {26, 36};

/* 2. 지워진 원래 코드 */

/* 새로 만들어진 코드 */
int New_WithinRange(TemperatureRange* tempRange) {
  return ((temperature_range->low >= heating_plan_temperature_range.low)
      && (temperature_range->high <= heating_plan_temperature_range.high));
}

TEST(PreserveWholeObject, WithinRangeFalse) {
  /* 지저분한 변수가 아닌 객체를 생성함 */
  TemperatureRange room_days_temperature_range = {25, 27};
  /* 여러 데이터의 객체를 통째로 넘김 */
  if (New_WithInRange(&room_days_temperature_range)) {
    printf("room temperature went outside range\n");
  }
  ASSERT_FALSE(New_WithInRange(&room_days_temperature_range));
}

 


IV. 기타

꼭 이 방법이 아닌, 기존 함수를 가지고 가면서 새로운 함수로 Wrapping 하는 방법도 있다.
-> 변수 / 함수 쪼개기

이는 코드를 작성하는 것이 아닌 순전히 다른 리팩토링만 연달아 수행한다.
-> 코드를 새로 작성하고 삭제하는 것이 아니라 그저 리팩토링.

예시.

// 1. 변수 추출하기
#include "gtest/gtest.h"

class TemperatureRange {
 public:
  int low;
  int high;
};

TemperatureRange heating_plan_temperature_range = {26, 36};

int WithinRange(int bottom, int top) {
  return (bottom >= heating_plan_temperature_range.low)
      && (top <= heating_plan_temperature_range.high);
}

// 이 TEST코드를 동작코드라고 생각하자
TEST(PreserveWholeObject, WithinRangeFalse) {
  TemperatureRange room_days_temperature_range = {25, 27};
  int low = room_days_temperature_range.low;
  int high = room_days_temperature_range.high;
  /* 1. 새롭게 추가된 변수 추출 */
  bool isWithinRange = WithinRange(low,high);
  
  if (isWithinRange) { /* 1. 변수로 대체 */
    printf("room temperature went outside range\n");
  }
  ASSERT_FALSE(WithinRange(low, high));
}
// 2. 함수 추출하기
#include "gtest/gtest.h"

class TemperatureRange {
 public:
  int low;
  int high;
};

TemperatureRange heating_plan_temperature_range = {26, 36};

int WithinRange(int bottom, int top) {
  return (bottom >= heating_plan_temperature_range.low)
      && (top <= heating_plan_temperature_range.high);
}

/* 2. 새로 쪼개진 함수 */
bool isWithinRange(TemperatureRange* tempRange) {
  int low = room_days_temperature_range.low;
  int high = room_days_temperature_range.high;
  bool isWithinRange = WithinRange(low,high);
  return isWithinRange;
}

// 이 TEST코드를 동작코드라고 생각하자
TEST(PreserveWholeObject, WithinRangeFalse) {
  TemperatureRange room_days_temperature_range = {25, 27};
  /* 2. 새로운 메서드로 추출하여 사용 */
  /*    그저 함수쪼개기로 나누었다.   */
  if (isWithinRange(room_days_temperature_range)) { 
    printf("room temperature went outside range\n");
  }
  ASSERT_FALSE(WithinRange(low, high));
}

 

+ Recent posts