现在推荐使用查询重构方法替换Temp,但是 似乎非常低效,收益很少。
来自Martin Fowler网站的方法给出了以下示例:
将表达式提取到方法中。用表达式替换对temp的所有引用。然后可以在其他方法中使用新方法。
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
变为
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
double basePrice() {
return _quantity * _itemPrice;
}
为什么这是个好主意?这肯定意味着计算不必要地重复,你有调用函数的开销。我知道CPU周期 很便宜但扔掉它们似乎不小心?
我错过了什么吗?
答案 0 :(得分:6)
阅读过Fowler的webpage on it之后,我认为这样做没有任何好处。唯一可能的收获是通过将可能经常使用的表达式隔离到一个点,但最好通过以下方式处理:
double basePrice = basePrice();
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
除了阅读他的书之外,福勒没有解释为什么他修改后的代码比原版更好。
答案 1 :(得分:4)
我认为这种重构的大部分效用并不是来自它自身的应用,而是作为向极端常见和有用的提取方法迈出的一步。在局部变量阻碍Extract方法或通过其他参数使其复杂化的情况下,'用查询替换temp有助于'使Extract Method更容易。同样在这些情况下,生成的查询方法由两种不同的方法重用,这两种方法是提取方法的结果。
答案 2 :(得分:4)
这是一个重要的重构,因为它是对单一责任的重构,它是DRY失败的解决方案!
temps的主要问题(特别是使用长方法的天真代码,长达数百行!)是它们是可变的本地状态。明显的风险(正如福勒所讨论的那样?)是有人可能会在漫长的方法中间做出改变,并在最后打破一些东西。 (已经看到了这笔费用)
没有测试,这种方法有无数的依赖 - 多么糟糕! :)
删除temp是关于重构单一责任。
示例 - 今天我发现了一个错误,它涉及将错误的临时变量传递给服务。如果我删除temps(有几个,所有字符串),那个bug(疏忽)就不会发生。
我的方法持有临时变量的原因是它正在做它不应该工作的......并且这个逻辑会被类似的类重复......它是否一致?没有!
通过删除temp,我还会将代码重构为具有适当职责的类,通过一些简单的测试可以覆盖110%。
没有巨大的夹具来测试一些微不足道的东西
如果我称之为昂贵的东西,我会将结果作为值对象/聚合返回。可能是'临时代码'应该由聚合内化。
所以删除临时推动你进入集中(单一)责任 - >固体!
答案 3 :(得分:4)
我现在正在重读“重构”,其中一个原因是Extract Method
第116页末尾的以下引用。
背景:他描述如果要提取的代码使用局部变量,则Extract Method
很难。
临时变量通常非常丰富,以至于它们使提取变得非常尴尬。在这些情况下,我尝试使用Replace Temp with Query(120)来减少临时值。 ...
因此,一次重构可能取决于首先进行另一次重构,而这种重构本身并不明显。另一个例子是在替换Inline Method
之前Method with Method Object
。
答案 4 :(得分:3)
它旨在更多地揭示代码的意图。在某些情况下,它可能被滥用但不太可能。例如,您可以使用查询更新查询以获得5%和2%的折扣,但是您可以在折扣的名称原因中描述方法的名称。记住,今天可能是显而易见的,但在6个月内可能不是这样 - 正如我所说 - 如果我忘记这不是问题,但是当我忘记时。
if (basePrice() > 1000)
return bigTicketDiscount()
else
return regularDiscount()
double bigTicketDiscount(){
return basePrice() * 0.95;
}
double regularDiscount(){
return basePrice() * 0.98
}
答案 5 :(得分:3)
还有更多关于在重构书中使用查询替换温度的内容,它是:
在这种情况下,您可能会担心表现。与其他性能问题一样,让它 暂时滑动。十分之九,没关系。什么时候重要,你会修复 优化期间的问题。通过更好地考虑代码,您将经常发现更强大的功能 优化,你可能会错过没有重构。如果情况变得更糟,那就是 很容易把温度回来。
答案 6 :(得分:1)
您(原始问问者和James Curran)都缺少在此特定示例中的计算方法。该代码将从if
的一个分支中下来,然后调用basePrice()
,以执行计算。或者,代码在if
的另一个分支下移,并且也在那里进行计算。
无论如何,乘法都会完成,但仍然只有一次。就像临时版本一样。
是的,很高兴知道默认情况下不缓存查询,而temp本质上是一种缓存形式。在此特定示例中,区分无关紧要。缓存(例如临时文件)的优势在于,它可以节省CPU周期(在这种情况下,实际上不是这样,就乘法本身而言,就调用,堆栈跳转等而言,然后确定……取决于编译器)。缓存的缺点是,如果您草率的话,它可能会使您过时或出现错误的数据。这是Fowler提到的特定临时缺点(它们在时间和词汇上都是局部的)。
我通常要做的是,大部分时间我都在查询中编码,并且当我看到我多次执行特定查询时(不同于本示例,它在单独的条件分支中),如果需要在本地使用临时文件,则可以使用临时文件;如果需要在更广的本地范围内访问临时文件,则可以引入备忘录或其他类型的缓存系统(例如简单的全局变量或类成员)。
那时,与这种重构相反的操作“用Temp替换查询”就派上用场了。
答案 7 :(得分:0)
在我看来,通过这样做,你有助于为你的原始方法做出一个单一的目的;它不再负责计算basePrice和折扣价格(或者它正在计算的任何东西)。这是牺牲一些CPU周期以使代码更易于维护的示例。 “浪费”的cpu周期可能不是你会注意到的,并且编译器也可以优化它(可能通过注意方法相对简单而不是调用方法它插入实际代码(更像是无论如何,这些类型的优化最好留给机器,让人们可以使用更清晰的代码。