2016-08-31 43 views
2

我有大約11功能看起來像這樣:許多非常相似的功能,意大利麪代碼修復?

def pending_acceptance(order_fulfillments) 
    order_fulfillments.each do |order_fulfillment| 
    next unless order_fulfillment.fulfillment_time_calculator. 
     pending_acceptance?; collect_fulfillments(
      order_fulfillment.status, 
      order_fulfillment 
     ) 
    end 
end 

def pending_start(order_fulfillments) 
    order_fulfillments.each do |order_fulfillment| 
    next unless order_fulfillment.fulfillment_time_calculator. 
     pending_start?; collect_fulfillments(
      order_fulfillment.status, 
      order_fulfillment 
     ) 
    end 
end 

迭代始終是相同的,但next unless條件是不同的。如果你想知道:它的next unless;在裏面,因爲RuboCop在抱怨它。有更好的實施方案嗎?我討厭這個意大利麪代碼。喜歡的東西傳遞到條件「iterate_it」功能或使...

編輯:不能只是通過另一個參數是因爲條件是雙有時:

def picked_up(order_fulfillments) 
     order_fulfillments.each do |order_fulfillment| 
     next unless 
      order_fulfillment.handed_over_late? && order_fulfillment. 
       fulfillment_time_calculator.pending_handover? 
       collect_fulfillments(
        order_fulfillment.status, 
        order_fulfillment 
       ) 
     end 
    end 

EDIT2:但有一個問題:我怎麼能切一個符號,從狀態獲得用戶角色?例如: :deliverer_started => :deliverer or 'deliverer'

+0

:傳遞一個參數並使用該參數作爲條件。通過使用這種方式,你將不得不使用1個功能,而不是11 –

+1

檢查https://codereview.stackexchange.com/,也:) – Felix

回答

1

可以使串

arr = ['acceptance','start', ...] 

在下一步的數組:

arr.each do |method| 

    define_method ('pending_#{method}'.to_sym) do |order_fulfillments| 
    order_fulfillments.each do |order_fulfillment| 
     next unless order_fulfillment.fulfillment_time_calculator. 
     send('pending_#{method}?'); collect_fulfillments(
      order_fulfillment.status, 
      order_fulfillment 
     ) 
    end 
    end 
end 

,瞭解更多有關define_method

+0

好的。我不明白爲什麼這個迴應已經被低估了。解決這個重複問題的最佳解決方案是使用元編程,並且這個解決方案正在做什麼。請在理解提議的解決方案之前不要冷靜下來!理解問題並提出一個好的解決方案需要時間 –

0

您可以使用類似method_missing

在類的底部,把這樣的事情:

def order_fulfillment_check(method, order_fulfillment) 
    case method 
    when "picked_up" then return order_fulfillment.handed_over_late? && order_fulfillment.fulfillment_time_calculator.pending_handover? 
    ... 
    ... [more case statements] ... 
    ... 
    else return order_fulfillment.fulfillment_time_calculator.send(method + "?") 
    end 
end 

def method_missing(method_name, args*, &block) 
    args[0].each do |order_fulfillment| 
    next unless order_fulfillment_check(method_name, order_fulfillment); 
    collect_fulfillments(
     order_fulfillment.status, 
     order_fulfillment 
    ) 
    end 
end 

根據您的要求,您可以如果METHOD_NAME以「pending_」開始檢查。

請注意,這段代碼沒有經過測試,但它應該在某個地方。

此外,作爲旁註,order_fulfillment.fulfillment_time_calculator.some_random_method實際上是違反了law of demeter。你可能想要解決這個問題。

+0

什麼是「+」?「'?可以通過那裏的另一個條件?另外,'&block'是什麼? – Giovani

+0

這是您當前方法的名稱,並在末尾添加'?',就像在您的代碼片段中一樣。在這種情況下'&block'不是必需的,而是一個編寫'method_missing'的通用約定,因爲它會捕獲所有在你的類中沒有定義的方法調用,即使你傳遞了一個塊。 – klaffenboeck

4

當您使用該參數來決定要檢查的條件時,可以傳遞另一個參數。只是存儲所有可能的條件lambda表達式在哈希:

FULFILLMENT_ACTIONS = { 
    pending_acceptance: lambda { |fulfillment| fulfillment.fulfillment_time_calculator.pending_acceptance? }, 
    pending_start:  lambda { |fulfillment| fulfillment.fulfillment_time_calculator.pending_acceptance? }, 
    picked_up:   lambda { |fulfillment| fulfillment.handed_over_late? && fulfillment.fulfillment_time_calculator.pending_handover? } 
} 

def process_fulfillments(type, order_fulfillments) 
    condition = FULFILLMENT_ACTIONS.fetch(type) 

    order_fulfillments.each do |order_fulfillment| 
    next unless condition.call(order_fulfillment) 
    collect_fulfillments(order_fulfillment.status, order_fulfillment) 
    end 
end 

被稱爲像:

process_fulfillments(:pending_acceptance, order_fulfillments) 
process_fulfillments(:pending_start, order_fulfillments) 
process_fulfillments(:picked_up, order_fulfillments) 
+0

看起來不錯! @spickermann我在「edit2」中添加了一個小問題。你會看看嗎? :) – Giovani

+0

您可以先將符號翻譯爲字符串:':deliverrer_started.to_s.slice('_')。first#=>'deliverrer''。我不確定這是否符合我的答案。 – spickermann

1

雖然next很方便在代碼中來得晚(R),因此是一個有點難度把握。我會先在列表中選擇,然後執行操作。 (請注意,這隻有在您的'支票'沒有像order_fullfillment.send_email_and_return_false_if_fails那樣的副作用時纔有可能)。

因此,如果測試可能很複雜,我會通過表達選擇標準,然後提取這些項目的處理(這些項目還匹配更多的方法名稱),開始重構,它可能看起來像中間的某個地方這樣的:

def pending_acceptance(order_fulfillments) 
    order_fulfillments.select do |o| 
    o.fulfillment_time_calculator.pending_acceptance? 
    end 
end 

def picked_up(order_fulfillments) 
    order_fulfillments.select do |order_fulfillment| 
    order_fulfillment.handed_over_late? && order_fulfillment. 
      fulfillment_time_calculator.pending_handover? 
    end 
end 

def calling_code 
    # order_fulfillments = OrderFulFillments.get_from_somewhere 
    # Now, filter 
    collect_fulfillments(pending_start  order_fulfillments) 
    collect_fulfillments(picked_up   order_fulfillments) 
end 

def collect_fullfillments order_fulfillments 
    order_fulfillments.each {|of| collect_fullfillment(of) } 
end 

您仍然有11(+1)的方法,但你到什麼恕我直言,你表達更多 - 和您的同事將神交發生什麼事快了。鑑於你的例子和問題,我認爲你應該瞄準一個簡單,富有表現力的解決方案。如果您更「硬核」,則使用其他解決方案中提供的更實用的lambda方法。另外請注意,這些方法可以合併(通過傳遞迭代器)。

相關問題