2016-02-25 104 views
0

我正在阅读一篇文章并且遇到了第一个示例代码。在模型中,实例变量被设置为避免不必要的查询。我还在其中一个栏目中看到了这一点(第二个例子)。另一方面,我读了更多的文章,如果我使用这种模式,那么我的应用程序可能不会线程安全,所以我不能利用我的彪马网络服务器。rails何时设置实例变量|| =

可以告诉我何时何地我应该使用这种模式?

1日例如:

def first_order_date 
    if first_order 
    first_order.created_at.to_date 
    else 
    NullDate.new 'orders' 
    end 
end 

private 

def first_order 
    @first_order ||= orders.ascend_by_created_at.first 
end 

第二个示例

def shipping_price 
    if total_weight == 0 
    0.00 
    elsif total_weight <= 3 
    8.00 
    elsif total_weight <= 5 
    10.00 
    else 
    12.00 
    end 
end 

def total_weight 
    @total_weight ||= line_items.to_a.sum(&:weight) 
end 

修订问题

1例

当我看到这个 'first_order_date' 总是在对象上调用(https://robots.thoughtbot.com/rails-refactoring-example-introduce-null-object ),所以我不完全明白额外的查询是如何实现的应避免。我敢肯定,我错了,但据我所知,这可能只是

def first_order_date 
    if orders.ascend_by_created_at.first 
    first_order.created_at.to_date 
    else 
    NullDate.new 'orders' 
    end 
end 

还是可以使用@first_order别的地方呢?

第二个示例

在原来问题的代码不等于这个?

def shipping_price 
    total_weight = line_items.to_a.sum(&:weight) 
    if total_weight == 0 
    0.00 
    elsif total_weight <= 3 
    8.00 
    elsif total_weight <= 5 
    10.00 
    else 
    12.00 
    end 
end 

我在这里看到他们与定义total_weight实现,但为什么它更好地在我的例子中使用实例变量?

回答

6

简短的故事是:你的代码应该罚款彪马。

关于彪马上下文中的线程安全性,你必须担心的是改变可能跨线程共享的东西(这通常意味着在一个级别而不是实例级别的东西 - 我不' t认为Puma会在它的线程中共享对象的实例) - 而且你没有这样做。

您引用的||=技术称为“memoization”。您应该阅读全文https://bearmetal.eu/theden/how-do-i-know-whether-my-rails-app-is-thread-safe-or-not/,特别是关于记忆的部分。

要回答的问题在您的意见:

  1. 为什么是不够的SHIPPING_PRICE方法的第一行定义total_weight = line_items.to_a.sum(&:weight)?当我看到它会运行查询一次

OK,所以如果shipping_price方法只能得到每该类的实例调用一次,那么你是对的 - 有没有必要记忆化。但是,如果多次调用每个实例,则每次都必须执行line_items.to_a.sum(&:weight)来计算总数。

所以我们假设你在同一个实例中由于某种原因连续三次调用shipping_price。然后没有备忘录,它将不得不执行3次。但随着记忆化,就不得不执行line_items.to_a.sum(&:weight)只有一次,接下来的两次它会只需要检索@total_weight实例变量的值

  • 在哪里使用'记忆'在你的轨道应用程序?
  • 嗯......我不知道我可以给一个很好的答案,如果没有写很长的答案和解释了很多背景等,但短期的故事是:只要有适合的方法下面的所有:每个实例

    • 可能被多次
    • 没有什么耗时(如查询数据库或东西)
    • 该方法的结果可以安全地高速缓存,因为它不太可能在d之间改变在不同的时间调用该方法(基于每个实例)

    一个很好的比喻可能是:如果有人问你时间,你检查你的手表(即耗时的动作)。如果他们再次问你时间,1秒钟后,你不需要再次检查你的手表 - 你基本上说“我已经检查了,现在是早上9点”。 (这有点像你在记忆时间 - 节省了你必须检查你的手表,因为自从你上次提问以来结果不会改变)。

    +0

    joshua,您是否也可以回答这些问题:1.为什么在shipping_price方法的第一行中定义'total_weight = line_items.to_a.sum(&:weight)'不够?正如我所看到的,它只会运行一次查询。 2.你在哪里使用轨道应用程序中的“memoization”? –

    +0

    我更新了我的答案。 –

    +0

    谢谢joshua。这样我就明白了。 –

    2

    在这种情况下,它用于避免在答案相同时重复执行代码。

    想象一下这个版本:

    def shipping_price 
        if line_items.to_a.sum(&:weight) == 0 
        0.00 
        elsif line_items.to_a.sum(&:weight) <= 3 
        8.00 
        elsif line_items.to_a.sum(&:weight) <= 5 
        10.00 
        else 
        12.00 
        end 
    end 
    

    这是一个简单的事情很多繁重的工作,是不是? ||=模式用于缓存结果。

    +0

    tadman,我知道使用它有什么意义。我不知道应该在何处/何时使用它。你能告诉我一些我应该避免这种情况的例子(例如确保代码是线程安全的)以及我应该使用的一些情况(在大多数rails应用程序中常见的一些例子)。 –

    +0

    是的,还有一件事。为什么仅仅在shipping_price方法的第一行中定义'total_weight = line_items.to_a.sum(&:weight)'是不够的? –

    +1

    回复:memoization的threadsafety,请阅读https://bearmetal.eu/theden/how-do-i-know-whether-my-rails-app-is-thread-safe-or-not/(有关于memoization的一节大约一半左右) –