2016-02-25 105 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的一節大約一半左右) –