企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
曾用名:以函数取代参数(Replace Parameter with Method) 反向重构:以参数取代查询(327) ![](https://box.kancloud.cn/056c15a412e5a8684b101d73c21a0224_403x135.jpeg) ``` availableVacation(anEmployee, anEmployee.grade); function availableVacation(anEmployee, grade) { // calculate vacation... ``` ![](https://box.kancloud.cn/a3bed334e2e1f6d1a46c5039deb25af9_91x152.jpeg) ``` availableVacation(anEmployee) function availableVacation(anEmployee) { const grade = anEmployee.grade; // calculate vacation... ``` ### 动机 函数的参数列表应该总结该函数的可变性,标示出函数可能体现出行为差异的主要方式。和任何代码中的语句一样,参数列表应该尽量避免重复,并且参数列表越短就越容易理解。 如果调用函数时传入了一个值,而这个值由函数自己来获得也是同样容易,这就是重复。这个本不必要的参数会增加调用者的难度,因为它不得不找出正确的参数值,其实原本调用者是不需要费这个力气的。 “同样容易”四个字,划出了一条判断的界限。去除参数也就意味着“获得正确的参数值”的责任被转移:有参数传入时,调用者需要负责获得正确的参数值;参数去除后,责任就被转移给了函数本身。一般而言,我习惯于简化调用方,因此我愿意把责任移交给函数本身,但如果函数难以承担这份责任,就另当别论了。 不使用以查询取代参数最常见的原因是,移除参数可能会给函数体增加不必要的依赖关系——迫使函数访问某个程序元素,而我原本不想让函数了解这个元素的存在。这种“不必要的依赖关系”除了新增的以外,也可能是我想要稍后去除的,例如为了去除一个参数,我可能会在函数体内调用一个有问题的函数,或是从一个对象中获取某些原本想要剥离出去的数据。在这些情况下,都应该慎重考虑使用以查询取代参数。 如果想要去除的参数值只需要向另一个参数查询就能得到,这是使用以查询取代参数最安全的场景。如果可以从一个参数推导出另一个参数,那么几乎没有任何理由要同时传递这两个参数。 另外有一件事需要留意:如果在处理的函数具有引用透明性(referential transparency,即,不论任何时候,只要传入相同的参数值,该函数的行为永远一致),这样的函数既容易理解又容易测试,我不想使其失去这种优秀品质。我不会去掉它的参数,让它去访问一个可变的全局变量。 ### 做法 - 如果有必要,使用提炼函数(106)将参数的计算过程提炼到一个独立的函数中。 - 将函数体内引用该参数的地方改为调用新建的函数。每次修改后执行测试。 - 全部替换完成后,使用改变函数声明(124)将该参数去掉。 ### 范例 某些重构会使参数不再被需要,这是我最常用到以查询取代参数的场合。考虑下列代码。 ##### class Order... ``` get finalPrice() {  const basePrice = this.quantity * this.itemPrice;  let discountLevel;  if (this.quantity > 100) discountLevel = 2;  else discountLevel = 1;  return this.discountedPrice(basePrice, discountLevel); } discountedPrice(basePrice, discountLevel) {  switch (discountLevel) {   case 1: return basePrice * 0.95;   case 2: return basePrice * 0.9;  } } ``` 在简化函数逻辑时,我总是热衷于使用以查询取代临时变量(178),于是就得到了如下代码。 ##### class Order... ``` get finalPrice() {  const basePrice = this.quantity * this.itemPrice;  return this.discountedPrice(basePrice, this.discountLevel); } get discountLevel() {  return (this.quantity > 100) ? 2 : 1; } ``` 到这一步,已经不需要再把`discountLevel`的计算结果传给`discountedPrice`了,后者可以自己调用`discountLevel`函数,不会增加任何难度。 因此,我把`discountedPrice`函数中用到这个参数的地方全都改为直接调用`discountLevel`函数。 ##### class Order... ``` discountedPrice(basePrice, discountLevel) {  switch (this.discountLevel) {   case 1: return basePrice * 0.95;   case 2: return basePrice * 0.9;  } } ``` 然后用改变函数声明(124)手法移除该参数。 ##### class Order... ``` get finalPrice() {  const basePrice = this.quantity * this.itemPrice;  return this.discountedPrice(basePrice, this.discountLevel); } discountedPrice(basePrice, discountLevel) {  switch (this.discountLevel) {   case 1: return basePrice * 0.95;   case 2: return basePrice * 0.9;  } } ```