脂肪模型重构的最佳实践

时间:2015-06-02 10:28:05

标签: ruby-on-rails ruby refactoring

我试图让我的胖User模型不那么笨重。我使用值对象来表示自定义值和对它们的操作,并且我坚持使用ActiveSupport::Concern和模块。我将this作为灵感来阅读。

我把这样的辅助方法:

def is_a_wizard?
  power_level >= WIZARD_POWER
end

def just_became_a_wizard?
  power_level == WIZARD_POWER
end

进入模块,并将它们作为一种扩展包含在内。但是,它很难阅读和维护,我在视图和控制器中都需要它们中的一些(例如用于向导身份验证)。我应该把它们放在哪里?为他们使用的时间创建服务对象?

4 个答案:

答案 0 :(得分:2)

您可以创建其他课程并在任何地方使用它:

# lib/wizard_detector.rb
class WizardDetector
  def initialize(power_level)
    @power_level = power_level
  end

  def is_a_wizard?
    @power_level >= WIZARD_POWER
  end

  def just_became_a_wizard?
    @power_level == WIZARD_POWER
  end 
end

# app/models/user.rb
class User

  delegate :is_a_wizard?, :just_became_a_wizard?, to: :wizard_detector

  def wizard_detector
    @wizard_detector ||= WizardDetector.new(power_level)
  end
end

# anywhere else
WizardDetector.new(power_level_to_check).is_a_wizard?

请注意,wizard_detector对象在模型中缓存,如果在请求流程中功率级别发生变化,则可能有害。可以替换缓存。

答案 1 :(得分:0)

正如我在评论中写的那样,模型中的业务逻辑方法是可以的。

但如果你觉得它太多了,你可以考虑像

这样的东西
class User < AR::Base

  def predicates
    @predicates ||= ::User::Predicates.new(self)
  end

end

并在/models/user/predicates.rb

class User
  class Predicates < SimpleDelegator
    def just_became_a_wizard?
      power_level == User::WIZARD_POWER
    end
  end
end

然后你可以这样做:

 user.predicates.just_became_a_wizard?

巨大的好处是您的用户模型不再使用大量方法。

缺点是每次都需要调用代理对象。

答案 2 :(得分:0)

我会重命名示例中的方法:

#is_a_wizard?会是#wizard吗? (这个将是红宝石的方式)

just_became_a_wizard? =&GT; new_wizard? (inexperienced_wizard?......这个不太明显)

计算机科学只有两件事:缓存失效和命名事物。

- Phil Karlton

答案 3 :(得分:0)

我最近开始越来越多地使用Concerns来保持模型清洁。我将为“Wizard”创建一个模块,在该命名空间下使用Concern模块为模型和控制器。虽然如果你有那么多与巫师有关的控制器方法,我个人会有点怀疑。对于观点,为什么不写标准助手呢?

当然,如果您有多个需要这些方法的模型,这更有意义,但它仍然可以帮助清理单个模型。我个人觉得很难维护,因为我几乎没有触及过这些小方法。您可能还会发现,除了方法定义之外,您还可以将其移动到关注点。

此外,您应该确保您的疑虑实际上是关注点,而不仅仅是您厌倦在模型中查看的方法的通用倾销场。