【问题标题】:Is subclassing a User model really bad to do in Rails?在 Rails 中子类化用户模型真的很糟糕吗?
【发布时间】:2011-05-26 23:41:45
【问题描述】:

我从 Rails 中得到了很多回击,因为我将 User 子类化为许多不同的子类。在我的应用程序中,并非所有用户都是平等的。实际上有很多模型对象,并不是每个用户类型都可以访问它们。

我还需要一种方法来执行多态行为。例如,许多方法的行为会因类型而异。这不就是多态性的用途吗?

但问题是,我总是受到 Rails 的反对。默认值——尤其是表单提交给参数哈希的方式——似乎像非子类模型一样工作。链接和参数哈希只是默认值真正咬你的两种方式。

在 Rails 中为不同类型的用户处理复杂逻辑的“正确”方法是什么?在 Java 中,子类化模型是有效的——您不必费力地让它以您想要的方式工作。但是在 Rails 中,很难让子类与 REST 约定一起使用,当您忘记包含 :as => :user 时它会惩罚您,或者当您将子类对象放入链接时会惩罚您,例如 edit_user_path(@user)

还有另一个领域也很难处理。假设我有一个Company 模型,它有很多Users。这些用户可以是导演、讲师、实习生等——所有不同的子类。

当我们创建帐户时,我们可能想要使用accepts_nested_attributes_for :users。但是,如果我们使用它,我们就不能指定它创建的类。该死的!

似乎 Rails 中的所有内容都旨在不希望您对模型进行子类化。如果您不进行子类化,那么一切都“正常工作”。但如果你继承类,你就下地狱了。

解决办法是什么?

【问题讨论】:

    标签: ruby-on-rails ruby model subclassing


    【解决方案1】:

    一般来说,Ruby 不鼓励继承,而是支持 mixin 行为和委托。 Ruby 和 Rails 可以做到这一点,但它往往会导致你提到的反击

    您的特定示例听起来像是委托的情况:有一个属于员工的 User 类(反之亦然)。该员工(例如主管、讲师等)的特定类型行为都在该特定员工类别中。然后用户将如何处理特定场景委托给它加入的员工

    【讨论】:

    • 我也完全同意这一点。我花了一段时间才打破总是将事物子类化并选择混合行为的习惯。不总是,但经常。
    • @russo:所以在我向某人发送复选标记之前要清楚,我所拥有的是一个未子类化的用户类......但我有一个被子类化的委托?那么如果用户是管理员,它只调用委托上可用的关联/方法,对于其他类型也是如此?
    • @russo:拥有一个名为 Role 的基本 ruby​​ 对象不是更好吗?它是子类化的而不是活动记录……并且每当实例化用户时,都会实例化这个对象。它基本上可以是一个策略/状态对象。那可能会更好吗?我问这些问题是因为它们对应用程序非常重要,如果可以避免的话,我不想弄错:)
    • 几乎...但没有子类! :) 你所说的类角色我会调用 Module EmployeeBehavior 或 UserBehavior,然后使用“include”将其混合到委托类中。该模块将仅包含各种代表的 100% 共享行为。然后每个委托类将包含它特有的任何其他行为。我完全明白你为什么要问!我绝对是通过艰难的方式学会了这一点。
    • @russo 好吧,我可能明白......有点。所以当User 对象被实例化时,我们看看它有哪些角色并动态包含模块?还是我们从一开始就包含所有模块?
    【解决方案2】:

    这是我想出来的一个技巧。不要假设这是预期的行为:

    class UserSubclass < User
    
      def self.model_name
        User.model_name
      end
    
    end
    

    默认情况下,所有模型(从 ActiveModel 派生)都根据具体的类名来标识自己。这是通过类方法#model_name 完成的(它返回ActiveModel::Name 的实例,self 作为参数。覆盖它以返回特定的类使Rails 走上正确的轨道。这样你就可以在你的模型中保留这个逻辑并且超出您的模板。

    【讨论】:

    • @d11wtq:这是一个巧妙的技巧。你如何处理像accepts_nested_attributes_for :users 这样的事情并且无法设置它创建的子类?
    • 我不必这样做,所以不太确定。您必须深入研究源代码并弄清楚 Rails 实际在做什么,然后在您认为合适的时候覆盖。不幸的是(功能,而不是错误......显然),Rails 实际上只记录使用他们认为是好的应用程序设计的做事方式,这可能不一定与其他人的思维方式一致。但通常你可以覆盖行为,而 Ruby 作为一门语言本身就是这样做的。
    • @d11wtq:我有一种感觉,它不想让我使用子类。我不知道您如何拥有 OO 语言并且不允许它。坦率地说,我开始认为我应该有一个 User 类,其中包含所有用户角色的每个 has_many/belongs_to 关联......然后如果人们不应该添加,就阻止他们添加。这可能是 Rails 想要的。不过这很愚蠢,因为用户将拥有太多他们不会/甚至无法使用的关联。该解决方案还将让我检查用户类型以确定对各种方法执行的操作,因为没有多态性
    • 如果您正在与 ActiveRecord 对抗,您可能会在 DataMapper 中获得更多乐趣。实际上我自己也使用 DM,因为我发现 ActiveRecord 限制太多,特别是考虑到我必须使用现有数据库,而不是新创建的数据库。 datamapper.org。它被分解成许多较小的项目,因此您必须在您的 Gemfile 中添加很多 dm-* gems,但这一切都很容易。 dm-rails 是您想要的 Rails 集成项目。
    • 另外,如果您喜欢 DataMapper 的外观并决定试一试,请记住,许多 gem 开发人员认为您使用的是 ActiveRecord。随着时间的推移,这会变得更好,因为 Rails 3 仍然很新,而且在 Rails 3 之前,像这样更换 ActiveRecord 并不容易。许多主流 gem 都明确支持 DataMapper 和 ActiveRecord,但并非所有较小的 gem 都支持。
    【解决方案3】:

    基本上是关于“说出你的意思”,

    当您说redirect_to @user 时,框架无法知道您是指通用用户表单还是专门的员工用户表单。

    这会产生很多redirect_to @user.becomes(User),您可以随意晾干

    def to_base_class
      becomes User
    end
    alias_method :to_b, :to_base_class
    

    当您的意图是重定向到 User 而不是 Employee 资源时,请写 redirect_to @user.to_b

    基本上,像redirect_to @user 这样的优雅语法表示模型和视图/控制器之间的非常深度耦合,并且当您使模型和视图/控制器逻辑变得更复杂时,由于这种耦合而导致的裂缝将开始显现并且需要在域分离方面做出一些额外的努力,或者需要编写更多代码。

    Rails 不会因为您使用 OOP 而惩罚您,您正在经历模型 视图 控制器关系的复杂性增加:

    曾几何时,你的模型是你的视图,反之亦然,现在你有两个模型类映射到两个视图类,如果你想为另一个模型使用一个视图,你必须说出来

    【讨论】:

      猜你喜欢
      • 2011-10-27
      • 1970-01-01
      • 1970-01-01
      • 2011-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-13
      相关资源
      最近更新 更多