좋은 소프트웨어의 핵심은 모듈성이라고 책에 나와있다. 모듈에 대해서는 최소한의 것만을 이해하고 그와 관련된 로직들은 내부에 깊이 숨겨두는 것이다.
그 모듈에 정의되어 있는 함수는 어떠한 컨텍스트 안에서 존재할텐데, 만약 다른 모듈을 더 많이 참조하고 있다면 이 함수를 옮겨주는 것이 타당하다.
이번 챕터는 한마디로 함수의 올바른 컨텍스트를 정하는 것이라고 해도 무방할 것 같다.
class Account {
get bankCharge() {
let result = 4.5;
if (this._daysOverdrawn > 0) result += this.overdraftCharge;
return result;
}
get overdraftCharge() {
if (this.type.isPremium) {
const baseCharge = 10;
if (this.daysOverdrawn <= 7) return baseCharge;
else return baseCharge + (this.daysOverdrawn - 7) * 0.85;
} else {
return this.daysOverdrawn * 1.75;
}
}
}
// 계좌 종류에 따라서 이자를 다르게 하기 위해, overdraftCharge메서드를 AccountType 클래스로 옮기자.
class AccountType {
overdraftCharge(daysOverdrawn) {
if (this.type.isPremium) {
const baseCharge = 10;
if (daysOverdrawn <= 7) return baseCharge;
else return baseCharge + (daysOverdrawn - 7) * 0.85;
} else {
return daysOverdrawn * 1.75;
}
}
}
class Account {
//...
// Account에서는 위임메서드를 통해서 결과값을 가져오면 된다.
get overdraftCharge() {
return this.type.overdraftCharge(this.daysOverdrawn);
}
}
module.exports = {};
function trackSummary(points) {
const totalTime = calculateTime();
const totalDistance = calculateDistance();
const pace = totalTime / 60 / totalDistance;
return {
time: totalTime,
distance: totalDistance,
pace,
};
function calculateDistance() {
let result = 0;
for (let i = 0; i < points.length; i++) {
result += distance(points[i - 1], points[i]);
}
return result;
}
function distance(p1, p2) {}
function radians(degrees) {}
function calculateTime() {}
}
// calculateDistance를 최상위로 옮겨보자.
function top_calculateDistance(points) {
let result = 0;
for (let i = 0; i < points.length; i++) {
result += distance(points[i - 1], points[i]);
}
return result;
}
// disatnce와 radians의 경우 trackSummary라는 최상위 함수 컨텍스트에 있는 어떤 것도
// 사용하지 않기 때문에 차라리 calculateDistacne함수 안으로 옮긴다.
function top_calculateDistance(points) {
let result = 0;
for (let i = 0; i < points.length; i++) {
result += distance(points[i - 1], points[i]);
}
return result;
function distance(p1, p2) {}
function radians(degrees) {}
}
// 원본함수에서 복사한 함수를 호출하자.
function trackSummary(points) {
const totalTime = calculateTime();
const totalDistance = calculateDistance();
const pace = totalTime / 60 / totalDistance;
return {
time: totalTime,
distance: totalDistance,
pace,
};
function calculateDistance() {
top_calculateDistance(points);
}
function calculateTime() {}
}
// 교체해주자. totalDistance로 이름을 바꾸고 변수를 인라인 해주자.
function trackSummary(points) {
const totalTime = calculateTime();
const pace = totalTime / 60 / totalDisatnce(points);
return {
time: totalTime,
distance: totalDisatnce(points),
pace,
};
function calculateTime() {}
}
// distance나 radians은 top_calculateDisatnce의 함수에 의존하고 있지 않으므로,
// 마찬가지로 최상위 함수로 옮긴다.
// 중첩함수는 데이터에 대한 결합과 의존성이 생기므로 되도록이면 피하자!
function trackSummary() {}
function totalDisatnce() {}
function distance() {}
function radians() {}
Refactoring: Improving the Design of Existing Code (2nd Edition) p278-288.
객체지향의 연장선에 있는 것 같다는 생각이 많이 들었다. 객체가 가지는 책임이 있고 그와 관련된 오퍼레이션들을 잘 모아둬서 응집력있는 코드를 작성하는 리팩토링 기법이라고 느꼈다. 특히 클래스를 활용한 예제에서는 객체의 역할에 따라서 public메서드로 메세지를 받을 수 있도록 코드를 수정했다는 것을 알 수 있었다.