이 글은 리팩터링 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));
}
'Programming > Refactoring(C++)' 카테고리의 다른 글
[Refactoring] 9. 플래그 인수 제거하기 (For Long Parameter) (0) | 2022.10.20 |
---|---|
[Refactoring] 8. 매개변수 객체 만들기 (For Long Parameter) (0) | 2022.10.19 |
[Refactoring] 6. 매개변수를 질의함수로 바꾸기 (For Long Parameter) (0) | 2022.10.18 |
[Refactoring] 5. 반복문 쪼개기 (For Long Function Smell) (0) | 2022.10.17 |