여러 함수를 변환 함수로 묶기

0

리팩터링 카탈로그

목록 보기
10/10

배경


소프트웨어는 데이터를 입력받아서 여러 가지 정보를 도출하곤 한다. 이렇게 도출된 정보는 여러 곳에서 사용될 수 있는데, 그러다 보면 이 정보가 사용되는 곳마다 같은 도출 로직이 반복되기도 한다. 이런 도출 작업들을 한데로 모으는게 좋다. 모아두면 검색과 갱신을 일관된 장소에서 처리할 수 있고 로직 중복도 막을 수 있다.

이렇게 하기 위한 방법으로 변환 함수를 사용할 수 있다. 변환 함수는 원본 데이터를 입력받아서 필요한 정보를 모두 도출한 뒤, 각각을 출력 데이터의 필드에 넣어 반환한다. 이렇게 해두면 도출 과정을 검토할 일이 생겼을 때 변환 함수만 살펴보면 된다.

이 리펙터링 대신 여러 함수를 클래스로 묶기로 처리해도 된다. 둘 사이에는 중요한 차이 하나 있다. 원본 데이터가 코드 안에서 갱신될 때는 클래스로 묶는 편이 훨씬 낫다. 변환 함수로 묶으면 가공한 데이터를 새로운 레코드에 저장하므로, 원본 데이터가 수정되면 일관성이 깨질 수 있기 때문에다.

여러 함수를 한데 묶는 이유 하나는 도출 로직이 중복되는 것을 피하기 위해서다. 이 로직을 함수로 추출하는 것만으로도 같은 효과를 볼 수 있지만, 데이터 구조와 이를 사용하는 함수가 근처에 있지 않으면 함수를 발견하기 어려울 때가 많다. 변환 함수(또는 클래스)로 묶으면 이런 함수들을 쉽게 찾아 쓸 수 있다.

절차


  1. 변환할 레코드를 입력받아서 값을 그대로 반환하는 변환 함수를 만든다
  • 이 작업은 대체로 깊은 복사로 처리해야 한다. 변환 함수가 원본 레코드를 바꾸지 않는지 검사하는 테스트를 마련해두면 도움될 때가 많다.
  1. 묶을 함수 중 함수 하나를 골라서 본문 코드를 변환 함수로 옮기고, 처리 결과를 레코드에서 새 필드로 기록한다. 그런 다음 클라이언트 코드가 이 필드를 사용하도록 수정한다.
  • 로직이 복잡하면 함수 추출하기부터 한다.
  1. 테스트한다.
  2. 나머지 관련 함수도 위 과정에 따라 처리한다.

예시


before

reading = {customer: "ivan", quantity:10, month:5, year=2021}
const aReading = acquireReading();

const base = calculateBaseCharge(aReading)
const taxableCharge = Math.max(0, base - taxThreshold(aReading.year));

function calculateBaseCharge(aReading) {
	return  baseRate(aReading.month, aReading.year) * aReading.quantity;
}

after

reading = {customer: "ivan", quantity:10, month:5, year=2021}
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);

const taxableCharge = aReading.taxableCharge;

function calculateBaseCharge(aReading) {
	return  baseRate(aReading.month, aReading.year) * aReading.quantity;
}

function enrichReading(original) {
	const result = _.cloneDeep(original);
     	result.baseCharge = calculateBaseCharge(result);
        result.taxableCharge =  Math.max(0, result.baseCharge - taxThreshold(result.year));
        return result;
}

출처


마틴 파울러 저 리팩터링 2판

0개의 댓글