✏️9.3 파생 변수를 질의 함수로 바꾸기 (Replace Derived Variable with Query)

리팩터링 전

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;}

🧷 배경

가변 데이터는 서로 다른 두 코드를 결합하기도 하는데, 한 쪽 코드에서 수정한 값이 연쇄 효과를 일으켜 다른 쪽 코드에 원인을 찾기 힘든 문제를 야기하기도 한다. 가변 데이터를 완전히 배제하기란 불가능하므로, 가변 데이터의 유효 범위를 가능한 좁히는 방법을 찾아야 한다.

🧷 절차

  1. 변수 값이 갱신되는 지점을 모두 찾는다. 필요하면 변수 쪼개기를 활용해 각 갱신 지점에서 변수를 분리한다.

  2. 해당 변수의 값을 계산해주는 함수를 만든다.

  3. 해당 변수가 사용되는 모든 곳에 어서션(assertion)을 추가(10.6절)하여 함수의 계산 결과가 변수의 값과 같은지 확인한다.

    • 필요하면 변수 캡슐화하기(6.6절)를 적용하여 어서션이 들어갈 장소를 마련해준다.

  4. 테스트한다.

  5. 변수를 읽는 코드를 모두 함수 호출로 대체한다.

  6. 테스트한다.

  7. 변수를 선언하고 갱신하는 코드를 죽은 코드 제거하기(8.9절)로 없앤다.

🧷 예시

// ProductionPlan 클래스 ...
get production() {return this._production;}
applyAdjustment(anAdjustment) {
    this._adjustments.push(anAdjustment);
    this._production += anAdjustment.amount;
}

데이터 중복을 줄여보자.

  1. 어셔선을 추가하여 이 값을 계산해낼 수 있는지 확인한다.

// ProductionPlan 클래스 ...
get production() {
    assert(this._production === this.caculatedProduction);
    return this._production;
}

get caculatedProduction() {
    return this._adjustments
        .reduce((sum, a) => sum += a.amount, 0);
}
  1. 어서션이 실패하지 않으면 필드를 반환하던 코드를 수정하여 계산 결과를 직접 반환하도록 한다.

  2. calculatedProduction() 메서드를 인라인한다.

  3. 옛 변수를 참조하는 모든 코드는 제거한다.

// ProductionPlan 클래스 ...
get production() {
    return this.caculatedProduction;
}

get caculatedProduction() {
    return this._adjustments
        .reduce((sum, a) => sum += a.amount, 0);
}

applyAdjustment(anAdjustment) {
    this._adjustments.push(anAdjustment);
}

🧷 예시: 소스가 둘 이상일 때

// ProductionPlan 클래스 ...
constructor(production) {
    this._production = production;
    this._adjustments = [];
}

get production() {return this._production;}

applyAdjustment(anAdjustment) {
    this._adjustments.push(anAdjustment);
    this._production += anAdjustment.amount;
}

어서션을 추가하면 _production의 초기값이 0이 아니면 실패한다.

  1. 변수 쪼개기를 먼저 적용한다.

// ProductionPlan 클래스 ...
constructor(production) {
    this._initialProduction = production;
    this.productionAccumulator = 0;
    this._adjustments = [];
}

get production() {
    return this._initialProduction + this._productionAccumulator;
}
  1. 어서션을 추가한다.

// ProductionPlan 클래스 ...
constructor(production) {
    this._initialProduction = production;
    this.productionAccumulator = 0;
    this._adjustments = [];
}

get calculatedProductionAcumulator() {
    return this._adjustments
        .reduce((sum, a) => sum + a.amount, 0);
}

이 다음은 이전과 거의 같다.

Last updated