개발/Refactoring
[리팩토링] Combine Functions into Transform 여러 함수를 변환 함수로 묶기
노는게제일좋아!
2021. 10. 19. 10:24
반응형
배경
- 여러 함수를 한데 묶는 이유 하나는 도출 로직이 중복되는 것을 피하기 위해서다.
- 이 리팩터링 대신 여러 함수를 "클래스로 묶기"로 처리해도 된다.
- 원본 데이터가 코드 안에서 갱신될 때 → 클래스로 묶기
- 원본 데이터가 수정되면 일관성이 깨지는 경우 → 변환 함수 사용
- 변환 함수로 묶으면 가공한 데이터를 새로운 레코드에 저장
const base(aReading) = {...}
const taxableCharge(aReading) = {...}
▼
function enrichReading(aReading){
const result = _.cloneDeep(original);
result.baseCharge = calculateBaseCharge(result);
result.taxableCharge = Math.max(0, result.baseCharge - taxThreshold(result.year);
return result;
}
절차
- 변환할 레코드를 입력받아서 값을 그대로 반환하는 변환 함수를 만든다.
- 묶을 함수 중 함수 하나를 골라서 본문 코드를 변환 함수로 옮기고, 처리 결과를 레코드에 새 필드로 기록한다. 그런 다음 클라이언트 코드가 이 필드를 사용하도록 수정한다.
- 테스트한다.
- 나머지 관련 함수도 위 과정에 따라 처리한다.
예시
- 영국 시민이 매달 마신 차의 양을 측정(reading)해서 세금을 부과하자.
-
const aReading = acquireReading(); // 측정량 const base = baseRate(aReading.month, aReading.year) * aReading.quantity; // 소비량 const taxableCharge = Math.max(0, base - taxThreshold(aReading.year); // 세금
- 이와 같은 계산 코드가 여러 곳에 반복된다면?!
-
const aReading = acquireReading(); // 측정량 const basicChargeAmount = calculateBaseCharge(aReading); // 소비량 function calculateBaseCharge(aReading){ return baseRate(aReading.month, aReading.year) * aReading.quantity; }
- 다른 곳에서 소비량을 계산하는 함수를 만들어 둔 것을 발견했다!
- 계산 코드가 중복되는 문제를 해결해 보자.
- 해결 방법: 다양한 파생 정보 계산 로직을 모두 하나의 변환 단계로 모을 것
-
- Step 1: 입력 객체를 그대로 복사해 반환하는 변환 함수 만들기
-
function enrichReading(aReading){ const result = _.cloneDeep(original); // 깊은 복사 return result; }
- * 참고: 본질은 같고 부가 정보만 덧붙이는 변환 함수의 이름을 "enrich"라 하고, 형태가 변할 때만 "transform"이라는 이름을 쓴다.
-
- Step 2: 변경하려는 계산 로직 중 하나를 고른다. 그리고 그 계산 로직에 측정값을 전달하기 전에 부가 정보를 덧붙이도록 수정한다.
-
const rawReading = acquireReading(); // 미가공 측정값 const aReading = enrichReading(rawReading); const basicChargeAmount = calculateBaseCharge(aReading); ◀︎ const taxableCharge = Math.max(0, base - taxThreshold(aReading.year);
-
function enrichReading(aReading){ const result = _.cloneDeep(original); result.baseCharge = calculateBaseCharge(result); ◀︎ return result; }
const rawReading = acquireReading(); const aReading = enrichReading(rawReading); const basicChargeAmount = aReading.baseCharge;
-
- Step 3: 테스트
- enrichReading()처럼 정보를 추가해 반환할 때 원본 측정값 레코드는 변경되지 않아야 한다.
-
it('check reading unchanged', function() { const baseReading = { customer: "ivan", quantity: 15, month: 5, year: 2017}; const oracle = _.cloneDeep(baseReading); enrichReading(baseReading); assert.deepEqual(baseReading, oracle); });
- Step 4: 나머지 관련 코드 처리
- 처음 코드
-
const aReading = acquireReading(); // 측정량 const base = (baseRate (aReading.month, aReading.year) * aReading.quantity); // 소비량 const taxableCharge = Math.max(0, base - taxThreshold(aReading.year); // 세금
-
- 현재까지 수정한 코드
-
const rawReading = acquireReading(); const aReading = enrichReading(rawReading); const taxableCharge = Math.max(0, aReading.baseCharge - taxThreshold(aReading.year); function enrichReading(aReading){ const result = _.cloneDeep(original); result.baseCharge = calculateBaseCharge(result); return result; }
-
- taxableCharge 계산 부분도 위 과정과 동일하게 수정해 준다.
-
const rawReading = acquireReading(); const aReading = enrichReading(rawReading); const taxableCharge = aReading.taxableCharge; function enrichReading(aReading){ const result = _.cloneDeep(original); result.baseCharge = calculateBaseCharge(result); result.taxableCharge = Math.max(0, result.baseCharge - taxThreshold(result.year); return result; }
- 테스트 성공하면 taxableCharge 변수도 인라인
- 처음 코드
반응형