在 mixins 中模块化代码的更好做法?还是直接将所有代码放入模型中?

Better practice to modularize code in mixins? Or just put all the code directly into the model?

标题说明了一切。我更喜欢将代码模块化为 mixin,并将它们包含到模型中。其他人喜欢将代码直接放入模型中,可能会将模型增长为 MOUS 的、异常大小的模型。

我想知道你们 do/what 在开发领域有什么更好的做法。

最近,我基本上将所有业务逻辑从模型中移出并移入普通的旧 Ruby 对象中 - 我猜这是事物的服务方法。我让 PORO 在我的应用程序中作为正式公民独立存在——我不会将它们混入。

将代码模块化作为封装独立关注点或行为的一种方式是一种很好的做法,但要小心将简短的 class 定义与“小”对象相关联。物体的“大小”最好通过暴露于其他物体的表面积来衡量;在 Ruby 的情况下,未标记为受保护或私有的方法是衡量这一点的好方法。

您还必须了解并考虑与其他对象紧密耦合的对象。当用作 mixins 时,模块可以帮助实现这一点,但情况并非总是如此。 ActiveRecord 将行为分解为 mixins 以便于组合(例如验证、回调和脏跟踪),但是 Base 对象以一种使 ActiveRecord 模型与底层代码库紧密耦合的方式组合这些。

这并不总是坏事。 David Heinemeier Hansson (@dhh) 提出了一个令人信服的案例,说明为什么这种“违规”有时可以创建非常有用、有益的界面和对象。

在实践中,我避免将很多额外的行为委托给 ActiveRecord 模型。当需要额外的行为时,可以将更具体的对象包裹在模型周围,在分离行为和代码的同时增强它们。例如:

class UserDecorator
  attr_reader :user
  delegate :first_name, :last_name, to: :user

  def initialize(user)
    @user = user
  end

  def name
    "#{first_name} #{last_name}"
  end
end

nameUser 对象上的一种非常常见的方法,但该行为与持久化的任何内容无关。通过将 user#name 添加到 ActiveRecord 对象的 public 接口,隐式期望 user#name= 也存在,并且这些映射到数据存储。通过将像这样的简单方法移动到更合适的对象,界面更清晰、更可扩展且更容易测试。

注意:如果您在 User 上定义了零个其他方法,并且您还没有使用装饰器,那么从在 [= 上定义 name 方法开始可能更有意义13=] 并在添加复杂性后提取它。寻求最复杂的解决方案是构建不必要的复杂应用程序的必经之路。 ;)