function setDimension(name, value) {
if (name === 'height') {
this._height = value;
return;
}
if (name === 'width') {
this._width = value;
return;
}
}
setDimension("height", 130)
setDimension("width", 50)
After
function setHeight(value) { this._height = value; }
function setWidth(value) { this._width = value; }
setHeight(130)
setWidth(50)
절차
플래그 인수에 대응하는 명시적 함수 생성
기존 코드를 명시적 함수를 호출하도록 수정
참고. 매개변수를 까다로운 방식으로 사용한다면 래핑 함수를 생각해보자.
function rushDeliveryDate(anOrder) {return deliveryDate(anOrder, true);}
function regularDeliveryDate(anOrder) {return deliveryDate(anOrder, false);}
객체 통째로 넘기기
레코드를 통째로 넘기면 변화에 대응하기 쉽다.
개요
Before
class HeatingPlan {
withinRange(bottom, top) {
return (bottom >= this._temperatureRange.low && top >= this._temperatureRange.high);
}
}
const low = aRoom.daysTempRange.low;
const high = aRoom.daysTempRange.high;
if (!aPlan.withinRange(low, high)) { alerts.push('방 온도가 지정 범위를 벗어났습니다.'); }
After
class HeatingPlan {
withinRange(aNumberRange) {
return (aNumberRange.low >= this._temperatureRange.low && aNumberRange.high >= this._temperatureRange.high);
}
}
if (!aPlan.withinRange(aRoom.daysTempRange)) { alerts.push('방 온도가 지정 범위를 벗어났습니다.'); }
절차
매개변수들을 원하는 형태로 받는 빈 함수 만들기
새 함수의 본문은 원래 함수를 호출하고, 새 매개변수와 원래 함수의 매개변수를 매핑
정적 검사 수행
새 함수를 호출하도록 수정
원래 함수를 인라인
새 함수의 이름을 적절히 수정하고 모든 호출자에 반영
참고. 추출과 인라인 리팩터링을 이용한 방법
class HeatingPlan {
xxNEWwithinRange(tempRange) { // 함수 추출하기
const low = tempRange.low; // 입력 매개변수 추출하기
const high = tempRange.high;
const isWithinRange = this.withinRange(low, high);
return isWithinRange;
}
withinRange(bottom, top) {
return (bottom >= this._temperatureRange.low && top >= this._temperatureRange.high);
}
}
const tempRange = aRoom.daysTempRange;
const isWithinRange = aPlan.xxNEWwithinRange(tempRange);
if (!isWithinRange) {
alerts.push('방 온도가 지정 범위를 벗어났습니다.');
}
매개변수를 질의 함수로 바꾸기
매개변수에서 얻을 수 있는 값을 별도 매개변수로 전달하는 것은 의미가 없다.
함수에 원치 않는 의존성이 생길 경우는 제외.
대상 함수는 참조 투명(함수에 똑같은 값을 건네 호출하면 항상 똑같이 동작)해야 한다.
반대 리팩터링 : 질의 함수를 매개변수로 바꾸기
개요
Before
availableVacation(anEmployee, anEmployee.grade);
function availableVacation(anEmployee, grade) {
// ...
}
let totalAscent = 0;
calculateAscent();
function calculateAscent() {
for (let i = 1; i < points.length; i++) {
const verticalChange = points[i].elevation - points[i - 1].elevation;
totalAscent += verticalChange > 0 ? verticalChange : 0;
}
}
After
const totalAscent = calculateAscent(); //.1, 3
function calculateAscent() { //.4
let result = 0; //.2
for (let i = 1; i < points.length; i++) {
const verticalChange = points[i].elevation - points[i - 1].elevation;
result += verticalChange > 0 ? verticalChange : 0;
}
return result; //.1
}
절차 (매 단계 테스트)
호출자는 함수가 반환하는 수정된 값을 자신의 변수에 저장하기
피호출 함수 안에 반환할 값을 가리키는 새로운 변수 선언하기
계산이 선언과 동시에 이루어지도록 통합하기 (+변수를 불변으로 만들기)
피호출 함수의 변수 이름을 새 역할에 어울리도록 수정하기
오류 코드를 예외로 바꾸기
예외는 프로그래밍 언어에서 제공하는 독립적인 오류 처리 메커니즘이다.
예외를 사용하면 오류 코드를 일일이 검사하거나 오류를 식별해 콜스택 위로 던지는 일을 신경쓰지 않아도 된다.
예외는 정확히 예상 밖의 동작일 때만 쓰자.
개요
Before
if (data)
return new ShippingRules(data);
else
return -23;
After
if (data)
return new ShippingRules(data);
else
throw new OrderProcessingError(-23);
절차 (매 단계 테스트)
콜스택 상위에 해당 예외를 처리할 예외 핸들러 작성하기
해당 오류 코드를 대체할 예외와 그 밖의 예외를 구분할 식별 방법 찾기
예외를 클래스 기반으로 처리할 수 있다면 서브클래스 만들기
정적 검사 수행하기
catch절을 수정하여 직접 처리할 수 있는 예외는 적절히 대처하고, 그렇지 않은 예외는 다시 던지기
오류 코드를 반환하는 곳 모두에서 예외를 던지도록 수정하기
오류 코드를 콜스택 위로 전달하는 코드 모두 제거하기
Example
class OrderProcessingError extends Error { // 2. 예외를 클래스 기반으로 처리
constructor(errorCode) {
super(`주문 처리 오류 ${errorCode}`);
this.code = errorCode;
}
get name() { return 'OrderProcessingError'; }
}
function localShippingRules(country) {
const data = countryData.shippingRules[country];
if (data) return new ShippingRules(data);
else throw new OrderProcessingError(-23); // 5. 오류 코드 대신 예외 클래스 사용
}
function calculateShippingCosts(anOrder) {
const shippingRules = localShippingRules(anOrder.country);
// ...
}
try { // 1. 예외 핸들러 작성하기
calculateShippingCosts(orderData);
} catch (e) {
if (e instanceof OrderProcessingError) // 4. 예외 클래스를 처리
errorList.push({order: orderData, errorCode: e.code,});
else
throw e;
}