【问题标题】:How to organize Rails models that are too fat?如何组织太胖的 Rails 模型?
【发布时间】:2025-12-09 16:20:06
【问题描述】:

将逻辑从控制器转移到模型中是一种很好的做法。但是在任何复杂的系统中,这总是会导致一个非常大的文件,即使大多数方法是按照 Rails 方式的一个衬里。

我已经将模型拆分为其他模块并将它们包含在原始模型中,例如model_flagsmodel_validation等。谁有更好的方法?

【问题讨论】:

    标签: ruby-on-rails model


    【解决方案1】:

    我意识到这是一个相当古老的问题,它已被标记为已回答,但它仍然有很好的 Google 果汁,所以我认为值得添加...

    Rails 3 引入了 ActiveSupport::Concern,可用于模块化跨模型共享的行为。或者,就此而言,为那些变得太胖的模特瘦身。

    DHH 自己在这里提供了一个简洁的示例要点:

    https://gist.github.com/1014971

    【讨论】:

    • 我从 Red Dot Ruby 大会上了解到这一点(我相信来自 Gregg Polack),所以是的,我相信未来这是一种更好的方式,可以很好地与 Rails 核心配合使用。跨度>
    • 注意:术语(问题,而不是答案)最终令人困惑。 ActiveSupport::Concern 用于编写具有可重用代码的模型(由许多模型),而模式 concerned_with 是一种将模型拆分为多个部分的简单方法。我喜欢第一个(因为它指示使用 app/ 而不是 lib/),我不确定第二个(看起来很方便,但在概念上很可疑)。
    • 我认为值得指出的是,担忧只是解决模型肥胖问题的一种方式。正如 srboisvert 所指出的,在选择将模型拆分为关注点之前,最好先质疑您的对象设计。这是一篇讨论该问题的文章:blog.codeclimate.com/blog/2012/10/17/…
    【解决方案2】:

    出于几个原因,我不会这样做。

    首先,您违反了事物将在它们应该在的地方的假设,这可能首先是 Rails 的最大好处。如果您将模型内容粘贴到模型中,一个新人可以很容易地进入您的项目并导航它。如果你把它拉出来,你只会增加延迟和一些混乱,特别是如果删除模块的唯一逻辑是减小模型大小。

    其次,你几乎一无所获,而你又失去了一些东西。如今,几乎所有编辑器和 IDE 都减轻了大文件的导航痛苦,因此文件大小已不再重要。将内容移至模块实际上会消除一些现代的便利性,并且需要您和您的同事或未来的维护人员在处理一个模型时跳过更多的文件。

    也就是说,我怀疑核心 Rails 最佳实践团队会告诉您的是,如果您的模型又大又复杂,那么您的设计就有缺陷,并且您的模型可能代表了可以制作成单独模型而不是模块的几个东西.

    【讨论】:

    • Rails 中的大多数行为都无法在“它们应该在的位置”找到。另外,通过将模块包含到主模型文件中,您已经在告诉其他人在哪里查看。我同意导航大文件并不难,但是您如何拆分模型?什么类型的方法先行?
    • 我不会拆分我的模型。我在按功能和按字母顺序组织它们之间摇摆不定,但我并不太担心。我同意 nakajima 的观点,即您应该尽可能地提取共享和可重用的功能,但更多的是为了 DRY 和良好的设计而不是长度。
    • 正如我在对自己答案的评论中指出的那样,我认为这是一个有效的观点,尤其是最后一段。在对象设计方面,没有什么是一成不变的。经常质疑您的设计并做出最佳判断。
    • 评估你的设计的一个非常简单的方法(最后一段)是通读你的代码,看看是否有经常弹出的名词。有时,如果您有几个方法引用同一个名词,则表明要创建一个对象/类。
    【解决方案3】:

    好吧,我不会说你们中的任何人将所有内容都放在一个模型中是错误的,但我认为能够分离各种关注点也是非常有效的。这至少是一种权衡。

    我正在发布我自己的问题的答案,因为我已经找到了 Rails 方式来做到这一点: http://github.com/jakehow/concerned_with

    更多信息可以在这里找到: http://m.onkey.org/2008/9/15/active-record-tips-and-tricks

    【讨论】:

      【解决方案4】:

      不了解您的对象模型,建议您有点困难,但我想说的是,如果您绝对确信所有验证/关联/回调需要那个地方,仍然有一些方法可以分解出常见的行为。因此,虽然我不会将一大段代码从一个文件移到另一个文件中,它只是重新打开类,但我会说使用模块/插件来描述常见类型的行为是一个好主意。

      例如,如果您正在构建 Facebook 式的活动提要,并且所有内容都需要生成“事件”,那么您可能希望将“可事件”行为移动到一个模块中,当包含该模块时,它定义了关联/验证/等。我想说这种方法实际上会提高代码的清晰度,因为在任何地方手动指定这些关联并不像将某些东西声明为 Eventable 那样富有表现力,也不安全(你会在很多地方重复逻辑,当逻辑发生变化时,剩下的你就知道了……)

      总而言之,我想说仔细看看您的对象模型。在您的测试套件中,如果您注意到所有测试都需要大量设置,那么这可能是一个很好的指标,表明您在对象模型中遗漏了一些东西。不过,再一次,一些示例代码会很棒。

      【讨论】:

        【解决方案5】:

        【讨论】:

          【解决方案6】:

          模块听起来很明智。我不会将方法调用(验证、回调、插件等)提取到模块中,但我会将提取限制为我自己的方法。

          和往常一样,如果您发布一些示例代码会有所帮助。我发现很难想象清理模型的通用策略,这取决于代码的性质。

          【讨论】:

          • 嗯,就是这样;我真的没有策略。目前,我只是将方法从文件转储到另一个文件,这感觉不太像 rails-ish,这就是为什么我在这里找出最佳实践的原因。干杯 =)