const basePrice = this._quantity * this._itemPrice;
if (basePrice > 1000)
  return basePrice * 0.95;
else
  return basePrice * 0.98;
 
 
  get basePrice() {this._quantity * this._itemPrice;}
 
...
 
if (this.basePrice > 1000)
  return this.basePrice * 0.95;
else
  return this.basePrice * 0.98;

动机

临时变量的一个作用是保存某段代码的返回值,以便在函数的后面部分使用它。临时变量允许我引用之前的值,既能解释它的含义,还能避免对代码进行重复计算。但尽管使用变量很方便,很多时候还是值得更进一步,将它们抽取成函数。

如果我正在分解一个冗长的函数,那么将变量抽取到函数里能使函数的分解过程更简单,因为我就不再需要将变量作为参数传递给提炼出来的小函数。将变量的计算逻辑放到函数中,也有助于在提炼得到的函数与原函数之间设立清晰的边界,这能帮我发现并避免难缠的依赖及副作用。

改用函数还让我避免了在多个函数中重复编写计算逻辑。每当我在不同的地方看见同一段变量的计算逻辑,我就会想方设法将它们挪到同一个函数里。

这项重构手法在类中施展效果最好,因为类为待提炼函数提供了一个共同的上下文。如果不是在类中,我很可能会在顶层函数中拥有过多参数,这将冲淡提炼函数所能带来的诸多好处。使用嵌套的小函数可以避免这个问题,但又限制了我在相关函数间分享逻辑的能力。

以查询取代临时变量(178)手法只适用于处理某些类型的临时变量:那些只被计算一次且之后不再被修改的变量。最简单的情况是,这个临时变量只被赋值一次,但在更复杂的代码片段里,变量也可能被多次赋值——此时应该将这些计算代码一并提炼到查询函数中。并且,待提炼的逻辑多次计算同样的变量时,应该能得到相同的结果。因此,对于那些做快照用途的临时变量(从变量名往往可见端倪,比如 oldAddress 这样的名字),就不能使用本手法。

做法

检查变量在使用前是否已经完全计算完毕,检查计算它的那段代码是否每次都能得到一样的值。

如果变量目前不是只读的,但是可以改造成只读变量,那就先改造它。

测试。

将为变量赋值的代码段提炼成函数。

如果变量和函数不能使用同样的名字,那么先为函数取个临时的名字。

确保待提炼函数没有副作用。若有,先应用将查询函数和修改函数分离(306)手法隔离副作用。

测试。

应用内联变量(123)手法移除临时变量。

范例

这里有一个简单的订单类。

class Order…

    constructor(quantity, item) {
    this._quantity = quantity;
    this._item = item;
  }
 
get price() {
    var basePrice = this._quantity * this._item.price;
    var discountFactor = 0.98;
    if (basePrice > 1000) discountFactor -= 0.03;
    return basePrice * discountFactor;
  }
}

我希望把 basePrice 和 discountFactor 两个临时变量变成函数。

先从 basePrice 开始,我先把它声明成 const 并运行测试。这可以很好地防止我遗漏了对变量的其他赋值点——对于这么个小函数是不太可能的,但当我处理更大的函数时就不一定了。

class Order…

   constructor(quantity, item) {
  this._quantity = quantity;
  this._item = item;
 }
 
 get price() {
  const basePrice = this._quantity * this._item.price;
  var discountFactor = 0.98;
  if (basePrice > 1000) discountFactor -= 0.03;
  return basePrice * discountFactor;
 }
}

然后我把赋值操作的右边提炼成一个取值函数。

class Order…

get price() {
 const basePrice = this.basePrice;
 var discountFactor = 0.98;
 if (basePrice > 1000) discountFactor -= 0.03;
 return basePrice * discountFactor;
}
 
 get basePrice() {
  return this._quantity * this._item.price;
 }

测试,然后应用内联变量(123)。

class Order…

get price() {
 const basePrice = this.basePrice;
 var discountFactor = 0.98;
 if (this.basePrice > 1000) discountFactor -= 0.03;
 return this.basePrice * discountFactor;
}

接下来我对 discountFactor 重复同样的步骤,先是应用提炼函数(106)。

class Order…

get price() {
 const discountFactor = this.discountFactor;
 return this.basePrice * discountFactor;
}
 
 get discountFactor() {
  var discountFactor = 0.98;
  if (this.basePrice > 1000) discountFactor -= 0.03;
  return discountFactor;
}

这里我需要将对 discountFactor 的两处赋值一起搬移到新提炼的函数中,之后就可以将原变量一起声明为 const。

然后,内联变量:

get price() {
  return this.basePrice * this.discountFactor;
}