许多非常相似的功能,意大利面条代码修复?

时间:2016-08-31 06:21:09

标签: ruby-on-rails ruby loops ruby-on-rails-4 iteration

我有大约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'

4 个答案:

答案 0 :(得分: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)

答案 1 :(得分: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

的更多信息

答案 2 :(得分: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方法。另请注意,可以组合这些方法(通过传递迭代器)。

答案 3 :(得分: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。你可能想要解决这个问题。