Replace Temp with Query,顾名思义,表示你用查询来替换临时变量本身,临时变量对于函数来说是只有当前函数可见的,如果你在同类的别的地方要用到这个变量你就必须重新写表达式来获取这个变量,这样的话你就会在不经意间让你的函数变得复杂起来,所以如果你想要使用Extract Method,那么Replace Temp with Query是必不可少的一个步骤。而我们前面介绍的Inline Temp其实是这个手法的一部分,两者的区别在于Inline Temp已经有了表达式自身,只需要做简单的替换就可以,表示用表达式本身把临时变量给去掉。而Replace Temp with Query更加全面,里面包含了提炼表达式到函数本身,然后替换引用点(Inline Temp)。如果你把所有的临时变量都替换为一个查询,你的类的结构和逻辑将非常清晰,这样将更加有利于你的重构和进行优化。
这个重构手法有个很重要的前提就是临时变量只能被赋值一次,或者赋值给临时变量的表达式不受别的条件约束进行改变。对于其他情况,可能你应该需要Split Temporary Variable或Separate Query from Modifer把情况弄的简单点之后再运用本手法。对于有那种收集结果的临时变量或者循环中要进行累积的变量,你需要将程序的逻辑复制到查询函数中去。
做法一般都是找到只被赋值一次的临时变量,然后用const加以修饰进行编译(表示之后没有对这个变量进行修改),然后将所有对这个变量等号右边的表达式提炼到一个函数中去(这个步骤其实已经是Inline Temp的前提条件),然后将这个函数先声明为private(如果以后有别的类需要再改为public,这样可以保证接口的整洁性),判断这个查询函数本身会不会修改对象内容,如果会的话就需要运用Separate Query from Modifer进行重构。这些步骤都做好之后,就可以用Inline Temp将之前做好的函数进行变量替换。
有些同学可能会担心性能问题,我明明一个变量放在那好好的,你不要用,导致你每次去使用多要做一次查询。对于这种情况大可放心,重构的目的是让程序更加清晰,有了更加清晰的程序之后再具体做优化也不迟,况且根据二八原则,仅仅这条查询语句倘若你系统真的出现了性能问题也不大可能,如果实在是因为这条语句,你也可以把变量再放回去。
下面来看下具体例子:
double getPrice() { int basePrice = m_quanity * m_itemPrice; double discountFactor; if (basePrice > 1000) { discountFactor = 0.95 } else { discountFactor = 0.98 } return basePrice * discountFactor; }
例子很简单,但是有两个临时变量,可以看到basePrice和discountFactor都被赋值了一次,如果我想重构这个函数,我们上面讲到了,Extract Method之前要做Replace Temp with Query,那么用Query来取代这两个临时变量那会让我们更加清晰重构的路线。
首先第一步,我们来进行basePrice的提炼,在之前加上const,进行编译,发现没有问题。
double getPrice() { const int basePrice = m_quanity * m_itemPrice; double discountFactor; if (basePrice > 1000) { discountFactor = 0.95 } else { discountFactor = 0.98 } return basePrice * discountFactor; }
然后我们将等号之后的提炼到独立小函数中去(为了确保查询函数本身的特质--不修改对象本身,我们这里可以利用C++特性给函数加上const限定)
int basePrice() const { return m_quanity * m_itemPrice; }
这样原来的函数就变成了
double getPrice() { const int basePrice = basePrice(); double discountFactor; if (basePrice > 1000) { discountFactor = 0.95 } else { discountFactor = 0.98 } return basePrice * discountFactor; }
然后逐步使用Inline Temp把对basePrice的地方进行替换并进行编译测试,最后直接把basePrice的声明去掉。重复这个动作来进行提炼discountFactor得到
double getPrice() const { return basePrice() * discountFactor(); } int basePrice() const { return m_quanity * m_itemPrice; } double discountFactor(){ if (basePrice() > 1000) { return 0.95 } else { return 0.98 }}
可以看到,在提炼discountFactor的时候,对于临时变量basePrice如果没有进行提炼,那么就需要将这个临时变量传进去。像这样
double discountFactor(int basePrice) { if (basePrice > 1000) { return 0.95 } else { return 0.98 } }
可以明显的看到,这个重构手法对于函数本身来说,提高了清晰度,也让我们进行后期重构能够更加便捷。