用查询替换Temp

11 java refactoring

现在推荐使用Query Temp with Query重构方法进行相当广泛的推广,但似乎非常低效,只获得很少的收益.

Martin Fowler网站的方法给出了以下示例:

将表达式提取到方法中.用表达式替换对temp的所有引用.然后可以在其他方法中使用新方法.

    double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000)
        return basePrice * 0.95;
    else
        return basePrice * 0.98;
Run Code Online (Sandbox Code Playgroud)

    if (basePrice() > 1000)
        return basePrice() * 0.95;
    else
        return basePrice() * 0.98;


double basePrice() {
    return _quantity * _itemPrice;
} 
Run Code Online (Sandbox Code Playgroud)

为什么这是个好主意?这肯定意味着计算不必要地重复,你有调用函数的开销.我知道CPU周期很便宜,但这样扔掉它们似乎不小心?

我错过了什么吗?

Ber*_*ann 7

我现在正在重新阅读 Refactoring,原因之一是Extract Methodp.116末尾的以下引用。

背景:他描述了Extract Method如果提取的代码使用局部变量很难。

临时变量通常非常丰富,以至于它们使提取变得非常困难。在这些情况下,我尝试通过使用 Replace Temp with Query (120) 来降低温度。...

因此,一次重构可能取决于首先进行另一次本身并不明显的重构。另一个例子是Inline Method在 Replace 之前Method with Method Object


Jam*_*ran 6

阅读过Fowler的网页后,我认为这样做没有任何好处.唯一可能的收获是通过将可能经常使用的表达式隔离到一个点,但最好通过以下方式处理:

    double basePrice = basePrice();
    if (basePrice > 1000)
            return basePrice * 0.95;
    else
            return basePrice * 0.98;
Run Code Online (Sandbox Code Playgroud)

除了阅读他的书之外,福勒没有解释为什么他修改后的代码比原版更好.


San*_*dra 6

您(最初的提问者和詹姆斯·柯兰)错过了在这个特定的例子中,计算没有重复。代码要么进入 的一个分支if,然后调用basePrice()来执行计算。或者代码进入另一个分支if并在那里执行计算。

\n

不管怎样,乘法都会进行,但仍然只进行一次。就像临时版本一样。

\n

是的,最好知道查询默认情况下不被缓存,并且临时本质上是缓存的一种形式。在这个特定的例子中,这种区别并不重要。缓存(例如临时)的优点是它可以提供CPU周期节省(在这种情况下并非如此,就乘法本身而言,我的意思是\ xe2 \ x80 \ x94在调用,堆栈跳转等方面,那么当然\xe2\x80\xa6 取决于编译器)。缓存的缺点是,如果您不小心,它可能会提供过时或错误的数据。除此之外,福勒还提到了临时变量的具体缺点(它们在时间上和词汇上都是本地的)。

\n

我通常做的是,我大部分时间都在查询中编写代码,当我看到我确实多次调用特定查询时(与此示例不同,它位于单独的条件分支中),我可以使用临时值(如果它是本地的)或引入的临时值如果我需要在更广泛、更少局部的范围内访问它,则可以使用记忆化或其他类型的缓存系统(例如简单的全局变量或类成员)。

\n

这时,与此重构相反的“用 Temp 替换查询”就派上用场了。

\n


der*_*rdo 5

我认为这种重构的大部分效用不是来自于它本身的应用,而是作为迈向极其常见和有用的提取方法的一步。在局部变量妨碍 Extract Method 或通过附加参数使其复杂化的情况下,“用查询替换 temp 帮助”使提取方法更容易。同样在这些情况下,生成的查询方法会被两种不同的方法重用,这些方法是提取方法的结果。


小智 5

在 Refactoring book 中还有更多关于 Replace Temp with Query 的内容,它是:

在这种情况下,您可能会担心性能。与其他性能问题一样,暂时让它滑动。十有八九,没关系。当它确实重要时,您将在优化期间解决问题。随着您的代码更好地分解,您通常会发现更强大的优化,如果不重构,您就会错过这些优化。如果情况变得更糟,很容易恢复温度。


小智 5

这是重要的重构,因为它是对单一职责的重构,它是DRY失败的解决方案!

临时文件的主要问题(特别是在使用长方法的幼稚代码中,长数百行!)是临时变量,它们是局部状态。明显的风险(如Fowler所讨论的那样)是,某人可能会在很长的方法中途进行更改,并最终破坏某些内容。(已看到产生的费用)

没有测试,此方法具有多种依赖性-真是一团糟!:)

删除临时人员是关于重构为单一职责。

示例-今天我发现了一个错误,它与将错误的temp变量传递到服务有关。如果我删除临时文件(有多个字符串),就不会发生该错误(疏忽)。

我的方法持有一个temp变量的原因是它在做它不应该做的工作...并且类似的类重复了此逻辑...是否全部一致?没有!

通过删除temp,我还将代码重构为具有适当职责的类,通过一些简单的测试即可覆盖110%。

没有巨大的工具可以测试琐碎的事情

如果我调用一些昂贵的东西,我将把结果作为值对象/集合返回。“临时代码”可能应该由集合内部化。

因此,删除temp将使您承担集中(单一)的职责-> SOLID!


Mik*_*keJ 3

其目的是更能揭示代码的意图。在某些情况下,它可能会被滥用,但可能性不大。例如,您可以使用查询来更新 5% 和 2% 折扣的查询,但使用您可以在折扣名称原因中描述的方法名称。请记住,今天可能很明显,但 6 个月后可能就不是这样了 - 正如我所说 - 问题不在于我是否忘记,而在于我什么时候忘记。

  if (basePrice() > 1000)
     return bigTicketDiscount()
  else
     return regularDiscount()

double bigTicketDiscount(){
  return basePrice() * 0.95;
}

double regularDiscount(){
  return basePrice() * 0.98
}
Run Code Online (Sandbox Code Playgroud)

  • 问题是,如果您阅读有关此事的 Fowler 页面(我的答案中的链接),您会看到问题中引用的代码(您暗示正在滥用该概念)直接来自 Fowler。你的建议似乎正在改变福勒所说的焦点。 (2认同)