以更 readable/maintainable 的方式重构 Rubocop Style/GuardClause

Refactoring Rubocop Style/GuardClause in a more readable/maintainable way

Rubocop 抱怨: Style/GuardClause:使用保护子句而不是将代码包装在条件表达式中。 如果 issue_flag == true && issue_notification_sent && !issue_notification_follow_up_sent && send_follow_up ^^

我的原码是

if issue_flag == true && issue_notification_sent && !issue_notification_follow_up_sent && send_follow_up
  email_address = "sales@test.com"
  puts "Emailing Follow Up #{email_address} - #{sales_order}"
  UserMailer.issue_notification(self, email_address).deliver_now
  update(issue_notification_follow_up_sent: true)
end

并且通过阅读 docs,我似乎可以通过实施以下代码来解决这个问题:

return unless issue_flag == true && issue_notification_sent && !issue_notification_follow_up_sent && send_follow_up
email_address = "sales@test.com"
puts "Emailing Follow Up #{email_address} - #{sales_order}"
UserMailer.issue_notification(self, email_address).deliver_now
update(issue_notification_follow_up_sent: true)

我可以看出,除非满足条件,否则这基本上会提前从方法中中断,但对我来说,这似乎不太可读。它似乎也不太易于维护,因为不能在此代码之后添加进一步的条件,除非它们在第一行传递条件,例如,如果 issue_flag == true && !issue_notification_sent 执行其他操作(任何匹配此条件的内容都已经在第 1 行返回)上面的重构代码)。

是否有更好的方法来重构它,以便可以在下面的代码之后添加更多条件,而不会过早返回代码?

谢谢。

我想我们可以做如下的事情

# issue_flag is boolean so we can directly put it
# create a new method with all the condition and give a proper name
return unless issue_flag && send_follow_up? # change name accourdingly
  email_address = "sales@test.com"
  puts "Emailing Follow Up #{email_address} - #{sales_order}"
  UserMailer.issue_notification(self, email_address).deliver_now
  update(issue_notification_follow_up_sent: true)
end
# document its behaviour
def send_follow_up?
  issue_notification_sent && !issue_notification_follow_up_sent && send_follow_up
end

guard 结构用于将控制发送到块之外,因此如果您需要更改某些内容或在条件之后执行某些操作,那么您将无法使用 guard 子句。看看下面的代码,在这种情况下我们不会使用保护子句

def test
  if something_here?
    do_something
  end

  do_something_else
  do_something_else1
  do_something_else2
end

我可能会将方法的大部分提取为私有方法,并使用清晰的名称来说明意图。伪代码实现如下所示:

def method_name
  return unless flag? && issue_notification_sent_with_follow_up

  log_follow_up
  UserMailer.issue_notification(self, @email_address).deliver_now
  update(issue_notification_follow_up_sent: true)
end

private

def flag?
  issue_flag == true
end

def issue_notification_sent_with_follow_up
  issue_notification_sent && !issue_notification_follow_up_sent && send_follow_up
end

def log_follow_up
  @email_address = "sales@test.com"
  puts "Emailing Follow Up #{@email_address} - #{sales_order}"
end