曾用名:移除对参数的赋值(Remove Assignments to Parameters)
曾用名:分解临时变量(Split Temp)
let temp = 2 * (height + width);
console.log(temp);
temp = height * width;
console.log(temp);
const perimeter = 2 * (height + width);
console.log(perimeter);
const area = height * width;
console.log(area);
变量有各种不同的用途,其中某些用途会很自然地导致临时变量被多次赋值,“循环变量”和“结果收集变量”就是两个典型例子。
除了这两种情况,还有很多变量用于保存一段冗长代码的运算结果,以便稍后使用。这种变量应该只被赋值一次。如果它们被赋值超过一次,就意味它们在函数中承担了一个以上的责任。如果变量承担多个责任,它就应该被替换(分解)为多个变量,每个变量只承担一个责任。
class Organization {
get name() {...}
}
class Organization {
get title() {...}
}
命名很重要,对于程序中广泛使用的记录结构,其中字段的命名格外重要。
数据结构是理解程序行为的关键。记录结构中的字段可能需要改名,类的字段也一样。在类的使用者看来,取值和设值函数就等于是字段。对这些函数的改名,跟裸记录结构的字段改名一样重要。
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;}
可变数据是软件中最大的错误源头之一。对数据的修改常常导致代码的各个部分以丑陋的形式互相耦合:在一处修改数据,却在另一处造成难以发现的破坏。完全去掉可变数据并不现实,但还是强烈建议:尽量把可变数据的作用域限制在最小范围。
有些变量其实可以很容易地随时计算出来,可以去掉这些变量。计算常能更清晰地表达数据的含义,而且也避免了“源数据修改时忘了更新派生变量”的错误。
有一种合理的例外情况:如果计算的源数据是不可变的,并且我们可以强制要求计算的结果也是不可变的,那么就不必重构消除计算得到的派生变量。
两种不同的编程风格:一种是对象风格,把一系列计算得出的属性包装在数据结构中;另一种是函数风格,将一个数据结构变换为另一个数据结构。
识别出所有对变量做更新的地方。如有必要,用拆分变量(240)分割各个更新点。
新建一个函数,用于计算该变量的值。
用引入断言(302)断言该变量和计算函数始终给出同样的值。
如有必要,用封装变量(132)将这个断言封装起来。
测试。
修改读取该变量的代码,令其调用新建的函数。
测试。
用移除死代码(237)去掉变量的声明和赋值。
反向重构:将值对象改为引用对象(256)
class Product{
applyDiscount(arg) {this._price.amount -= arg;}
// ...
}
class Product {
applyDiscount(arg) {
this._price = new Money(this._price.amount - arg, this._price.currency);
}
// ...
}
在把一个对象(或数据结构)嵌入另一个对象时,位于内部的这个对象可以被视为引用对象,也可以被视为值对象。两者最明显的差异在于如何更新内部对象的属性:如果将内部对象视为引用对象,在更新其属性时,保留原对象不动,更新内部对象的属性;如果将其视为值对象,替换整个内部对象,新换上的对象会有想要的属性值。
如果想在几个对象之间共享一个对象,以便几个对象都能看见对共享对象的修改,那么这个共享的对象就应该是引用。
一般说来,不可变的数据结构处理起来更容易。可以放心地把不可变的数据值传给程序的其他部分,而不必担心对象中包装的数据被偷偷修改。
反向重构:将引用对象改为值对象(252)
let customer = new Customer(customerData);
let customer = customerRepository.get(customerData.id);
一个数据结构中可能包含多个记录,而这些记录都关联到同一个逻辑数据结构。
如果共享的数据需要更新,将其复制多份的做法就会遇到巨大的困难。漏掉一个副本没有更新,就会遭遇麻烦的数据不一致。
把值对象改为引用对象会带来一个结果:对于一个客观实体,只有一个代表它的对象。这通常意味着会需要某种形式的仓库,在仓库中可以找到所有这些实体对象。只为每个实体创建一次对象,以后始终从仓库中获取该对象。