get discountedTotal() {return this._discountedTotal;}
set discount(aNumber) {
const old = this._discount;
this._discount = aNumber;
this._discountedTotal += old - aNumber;
}
리팩터링 후
get discountedTotal() {return this._baseTotal - this._discount;}
set discount(aNumber) {this._discount = aNumber;}
🧷 배경
가변 데이터는 서로 다른 두 코드를 결합하기도 하는데, 한 쪽 코드에서 수정한 값이 연쇄 효과를 일으켜 다른 쪽 코드에 원인을 찾기 힘든 문제를 야기하기도 한다. 가변 데이터를 완전히 배제하기란 불가능하므로, 가변 데이터의 유효 범위를 가능한 좁히는 방법을 찾아야 한다.
🧷 절차
변수 값이 갱신되는 지점을 모두 찾는다. 필요하면 변수 쪼개기를 활용해 각 갱신 지점에서 변수를 분리한다.
해당 변수의 값을 계산해주는 함수를 만든다.
해당 변수가 사용되는 모든 곳에 어서션(assertion)을 추가(10.6절)하여 함수의 계산 결과가 변수의 값과 같은지 확인한다.
필요하면 변수 캡슐화하기(6.6절)를 적용하여 어서션이 들어갈 장소를 마련해준다.
테스트한다.
변수를 읽는 코드를 모두 함수 호출로 대체한다.
테스트한다.
변수를 선언하고 갱신하는 코드를 죽은 코드 제거하기(8.9절)로 없앤다.
🧷 예시
// ProductionPlan 클래스 ...
get production() {return this._production;}
applyAdjustment(anAdjustment) {
this._adjustments.push(anAdjustment);
this._production += anAdjustment.amount;
}
데이터 중복을 줄여보자.
어셔선을 추가하여 이 값을 계산해낼 수 있는지 확인한다.
// ProductionPlan 클래스 ...
get production() {
assert(this._production === this.caculatedProduction);
return this._production;
}
get caculatedProduction() {
return this._adjustments
.reduce((sum, a) => sum += a.amount, 0);
}
어서션이 실패하지 않으면 필드를 반환하던 코드를 수정하여 계산 결과를 직접 반환하도록 한다.
calculatedProduction() 메서드를 인라인한다.
옛 변수를 참조하는 모든 코드는 제거한다.
// ProductionPlan 클래스 ...
get production() {
return this.caculatedProduction;
}
get caculatedProduction() {
return this._adjustments
.reduce((sum, a) => sum += a.amount, 0);
}
applyAdjustment(anAdjustment) {
this._adjustments.push(anAdjustment);
}