簡體   English   中英

如何在Ruby中重構長if / return語句?

[英]How to refactor long if/return statements in Ruby?

我經常遇到一堆復雜的if語句,Ruby可以用什么方式清理它?

(在此服務對象示例中,一個foo有很多小節。這是用於將小節轉移到另一個foo。)

class BarManager
  include FancyErrorLogger

  def self.transfer(bar, new_foo)

    # Is a move needed? Is this line superfluous and a premature optimisation?
    return true if bar.foo_id == new_foo.id

    # Checks that bar can be moved to new_foo. Many more elsifs in practice, needs refactoring. These examples demonstrate the potential complexity of each step, preventing the use of overly simplistic solutions such as seen here http://codereview.stackexchange.com/questions/14080/avoiding-a-lot-of-ifs-in-ruby
    if bar.dependency == :do_not_move_me or bar.some_condition == false
      bar.errors.add( :transfer, "This is the bar that can't be moved, it is on street corners moping and singing")
      return false
    elsif new_foo.want_more_bars == false
      bar.errors.add( :transfer, "\"We don't take kindly to your type, bar\" - #{new_foo.name}")
      return false
    elsif ((bar.baz.optional_external_nightmare_status != :unused) || (new_foo.bars.inject(false){|result, element| result = result || element.baz.optional_external_nightmare_status != :unused}))
      bar.errors.add( :transfer, "I have some baz news for you...")
      return false
    elsif bar.yet_another_failure_reason_there_are_many
      bar.errors.add( :transfer, "There are many ways for this to fail, this if statement is somewhat short")
      return false
    elsif bar.stubborn?
      bar.lure_with_carrot!
      if bar.munching?
        bar.errors.add ( :transfer, "eh, what's up doc" )
        return false
      elsif !bar.following?
        bar.errors.add ( :transfer, "Your carrot is too small and inadequate. No jokes please" )
        return false
      end
    end

    # We made it through the gauntlet, now for the transfer
    cache_old_foo_id = bar.foo_id # we might need this
    bar.foo_id = new_foo.id
    bar.save!

    # If we are using Rails counter caching:
    Foo.increment_counter(:bars_count, new_foo.id)
    Foo.decrement_counter(:bars_count, cache_old_foo_id) # we DID need it

    return true

  rescue Exception => e
    fancy_error_log e
    false
  end

end

步驟1是從該long if語句中刪除重復項。 if返回一個值( if / elsif匹配, if返回nil;否則,則返回nil)

error =
  if bar.dependency == :do_not_move_me or bar.some_condition == false
    "This is the bar that can't be moved, it is on street corners moping and singing"
  elsif new_foo.want_more_bars == false
    "\"We don't take kindly to your type, bar\" - #{new_foo.name}")
  elsif ((bar.baz.optional_external_nightmare_status != :unused) || (new_foo.bars.inject(false){|result, element| result = result || element.baz.optional_external_nightmare_status != :unused})
    "I have some baz news for you..."
  elsif bar.yet_another_failure_reason_there_are_many
    "There are many ways for this to fail, this if statement is somewhat short"
  elsif bar.stubborn?
    bar.lure_with_carrot!
    if bar.munching?
      "eh, what's up doc"
    elsif !bar.following?
      "Your carrot is too small and inadequate. No jokes please"
    end
  end
if error
  bar.errors.add :transfer, error
  return false
end

步驟2是將條件提取到名稱可以理解的方法中,無論如何這都是一個好主意。 甚至將每個條件下的所有內容都移動到其方法中! 運算符,並為每個方法提供相同的參數列表。 我們一會兒會明白為什么。

error =
  if bar.unmovable? new_foo
    "This is the bar that can't be moved, it is on street corners moping and singing"
  elsif bar.unwanted? new_foo
    "\"We don't take kindly to your type, bar\" - #{new_foo.name}")
  elsif bar.has_bad_news? new_foo
    "I have some baz news for you..."
  elsif bar.will_fail_for_yet_another_reason? new_foo
    "There are many ways for this to fail, this if statement is somewhat short"
  elsif bar.lure_with_carrot? new_foo # if you don't like the side effects, lure it before the if
    "eh, what's up doc"
  elsif bar.uninterested_in_carrot? new_foo
    "Your carrot is too small and inadequate. No jokes please"
  end
# use error as above

步驟3是再次刪除重復項:

checks = {
  unmovable?: "This is the bar that can't be moved, it is on street corners moping and singing",
  unwanted?: "\"We don't take kindly to your type, bar\" - #{new_foo.name}"),
  has_bad_news?: "I have some baz news for you...",
  will_fail_for_yet_another_reason?: "There are many ways for this to fail, this if statement is somewhat short",
  lure_with_carrot?: "eh, what's up doc",
  uninterested_in_carrot?: "Your carrot is too small and inadequate. No jokes please"
}
method_name, error = checks.find { |method_name, _| bar.send :method_name, new_foo }
# use error as above

我將使用特殊的異常類型:

begin 
  fail BarTransferError, "This is the bar that can't be moved, it is on street corners moping and singing" if bar.dependency == :do_not_move_me or bar.some_condition == false
  fail BarTransferError, "\"We don't take kindly to your type, bar\" - #{new_foo.name}" if new_foo.want_more_bars == false
  fail BarTransferError, "I have some baz news for you..." if ((bar.baz.optional_external_nightmare_status != :unused) || (new_foo.bars.inject(false){|result, element| result = result || element.baz.optional_external_nightmare_status != :unused}))
  fail BarTransferError, "There are many ways for this to fail BarTransferError,, this if statement is somewhat short" if bar.yet_another_fail BarTransferError,ure_reason_there_are_many

  if bar.stubborn?
    bar.lure_with_carrot!
    fail BarTransferError, "eh, what's up doc" if bar.munching?
    fail BarTransferError, "Your carrot is too small and inadequate. No jokes please" unless bar.following?
  end
rescue BarTransferError => e
  bar.errors.add(:transfer, e.message)
  return false
end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM