리팩터링 | 마틴 파울러 | 한빛미디어- 교보ebook

코드 구조를 체계적으로 개선하여 효율적인 리팩터링 구현하기, 20여 년 만에 다시 돌아온 마틴 파울러의 리팩터링 2판 리팩터링 1판은 1999년 출간되었으며, 한국어판은 2002년 한국에 소개되었다

ebook-product.kyobobook.co.kr

개요

소프트웨어는 데이터를 입력받아서 여러 가지 정보를 도출하기도 한다.

도출 정보는 여러 곳에서 사용된다. 이런 반복 도출 작업을 곳에 모아두면 중복을 막을 있다.

 

방법으로 변환 함수가 있다.

원본 데이터를 입력받아 필요한 정보를 모두 도출한 , 각각 출력 데이터의 필드에 넣어 반환한다.

 

리팩터링 대신 "여러 함수를 클래스로 묶기" 처리해도 된다.

 

원본 데이터가 코드 안에서 갱신될 때는 클래스로 묶는 편이 데이터 일관성 유지 때문에 좋다.

변환 함수는 가공한 데이터를 새로운 레코드에 저장하기에 일관성이 깨질 있다.

 

여러 함수를 묶는 이유는 로직 중복 회피다.

 

예시

차를 수돗물 처럼 제공하는 서비스 제공을 가정한다.

수돗물처럼 계량해 비용을 산정해야 한다.

//차 사용량 레코드
reading = {customer: "ivan", quantity:10, month: 5, year:2017};


//client1.js  기본 요금 계산 코드
const aReading = acquireReading();
const baseCharge = baseRate(aReading.month, aReading.year) *aReading.quantity;


//client2.js  차 세금 일부 면제 코드
const aReading = acquireReading();
const base = (baseRate(aReading.month, aReading.year) *aReading.quantity);
const taxableCharge = Math.max(0, base - taxThreshold(aReading.year));


/** client3.js
 * 중복코드가 한 곳에 몰려있으면 함수 추출로 제거하면 된다.
 * 현재 중복 코드가 전체에 흩어져 있다고 가정한다.
 */
const aReading = acquireReading();
const basicChargeAmount = calculateBaseCharge(aReading);
//이미 함수로 만든 코드가 발견됐다.
function calculateBaseCharge(aReading){
    return baseRate(aReading.month, aReading.year) * aReading.quantity;
}
//먼저 입력 객체를 그대로 복사해 반환하는 함수를 만든다.
function enrichReading(original){
    const result = _.cloneDeep(original);
    return result;
}
const rawReading = acquireReading(); //미가공 측정값
const aReading = enrichReading(rawReading);
const basicChargeAmount = calculateBaseCharge(aReading);
function enrichReading(original){
    const result = _.cloneDeep(original);
    //미가공 측정값에 기본 소비량을 부가 정보로 덧붙임
    result.baseCharge =  calculateBaseCharge(result );
    return result;
}
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const basicChargeAmount = aReading.baseCharge;
//enrichReading() 함수가 정보추가 시 원본 함수를 훼손하는지 체크
it('check reading unchanged', function(){
    const baseReading = reading = {customer: "ivan", quantity:10, month: 5, year:2017};
    const oracle = _.cloneDeep(baseReading);
    enrichReading(baseReading);
    assert.deepEquals(baseReading, oracle);
})
//client1도 수정
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const baseCharge = aReading.baseCharge;
//client2.js  차 세금 일부 면제 코드
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const base = aReading.baseCharge;
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(original){
    const result = _.cloneDeep(original);
    result.baseCharge =  calculateBaseCharge(result);
    //함수 이동
    result.taxableCharge = Math.max(0, result.baseCharge - taxThreshold(result.year));
    return result;
}


//client2.js  차 세금 일부 면제 코드
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const taxableCharge = aReading.taxableCharge;
//전체
reading = {customer: "ivan", quantity:10, month: 5, year:2017};
//client1
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const baseCharge = aReading.baseCharge;
//client2.js  차 세금 일부 면제 코드
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const taxableCharge = aReading.taxableCharge;
//client3
const rawReading = acquireReading();
const aReading = enrichReading(rawReading);
const basicChargeAmount = aReading.baseCharge;
function enrichReading(original){
    const result = _.cloneDeep(original);
    result.baseCharge =  calculateBaseCharge(result);
    //함수 이동
    result.taxableCharge = Math.max(0, result.baseCharge - taxThreshold(result.year));
    return result;
}
//이미 함수로 만든 코드가 발견됐다.
function calculateBaseCharge(aReading){
    return baseRate(aReading.month, aReading.year) * aReading.quantity;
}
//enrichReading() 함수가 정보추가 시 원본 함수를 훼손하는지 체크
it('check reading unchanged', function(){
    const baseReading = reading = {customer: "ivan", quantity:10, month: 5, year:2017};
    const oracle = _.cloneDeep(baseReading);
    enrichReading(baseReading);
    assert.deepEquals(baseReading, oracle);
})

 

측정값에 부가 정보를 추가하는 지금 방식은 클라이언트가 데이터를 변경하면 데이터 일관성이 깨져 심각한 문제가 생길 있다.

자바스크립트에서 이런 문제를 방지하기 좋은 방법은 여러 함수를 클래스로 묶기다.

불변 데이터 구조를 지원하는 언어라면 이런 문제가 생길 일이 없어 여러 함수를 변환 함수로 묶기를 병용해서 사용해도 괜찮다.

불변성을 제공하지 않은 언어라도 데이터를 읽기전용으로 사용될 때는 변환 함수 사용도 괜찮다.

 

+ Recent posts