관리 메뉴

너와 나의 스토리

[리팩토링] Combine Functions into Transform 여러 함수를 변환 함수로 묶기 본문

개발/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;
}

 

절차

  1. 변환할 레코드를 입력받아서 값을 그대로 반환하는 변환 함수를 만든다.
  2. 묶을 함수 중 함수 하나를 골라서 본문 코드를 변환 함수로 옮기고, 처리 결과를 레코드에 새 필드로 기록한다. 그런 다음 클라이언트 코드가 이 필드를 사용하도록 수정한다.
  3. 테스트한다.
  4. 나머지 관련 함수도 위 과정에 따라 처리한다.

 

예시

  • 영국 시민이 매달 마신 차의 양을 측정(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 변수도 인라인

 

 

반응형
Comments