Rails STI - 防止基类实例化

Posted

技术标签:

【中文标题】Rails STI - 防止基类实例化【英文标题】:Rails STI - Prevent base class from instantiation 【发布时间】:2010-05-17 15:31:49 【问题描述】:

在 Rails STI 情况下,当基类被实例化时,有什么方法可以引发错误?覆盖初始化会做到这一点,但随后会渗透到子类。

谢谢

【问题讨论】:

【参考方案1】:

John Topley 的回答实际上是错误的。在基类中设置 abstract_class= true 实际上会导致子类停止自动设置它们的类型。另外,除非你在基类中设置_table_name,否则子类会抱怨他们的表不存在。

这是因为 abstract_class=true 的目的是在您不使用 STI 并希望在 ActiveRecord::Base 和一个或多个模型类。

初始化 raise 是一种解决方案,将 validates_presence_of :type 添加到基类也是一种解决方案。

注意如果你重写了初始化,你需要调用 super:

def initialize(*args)
  raise "Cannot directly instantiate an AbstractUser" if self.class == AbstractUser
  super
end

【讨论】:

【参考方案2】:

你可以试试这个:

class BaseClass
  def initialize
    raise "BaseClass cannot be initialized" if self.class == BaseClass
  end
end

class ChildClass
end

结果将是:

a = BaseClass.new  # Runtime Error
b = ChildClass.new # Ok

希望有帮助

【讨论】:

由于 BaseClass 可能会从 ActiveRecord::Base 继承,所以 initialize 可能应该调用 super。【参考方案3】:

我通常更喜欢简单地将 new 类方法设为私有:

class Base
  private_class_method :new 
end

这种方式意外实例化 Base 类会触发错误,但仍然可以使用 Base.send(:new) 实例化它来为 Base 类编写测试。

【讨论】:

【参考方案4】:

比验证存在性更好的是根据已接受的非抽象类类型的已知列表进行验证

  validates :type, :inclusion=>  :in => ["A", "B", "C"] 

因为如果您只是为了存在而进行验证,“邪恶的开发者”仍然可以将抽象类名作为类型参数传递。

【讨论】:

【参考方案5】:

在初始化函数中检查该类是 STI 基类。

虽然问题是您为什么要这样做?尝试不同的设计似乎更有可能对您有更多帮助。

【讨论】:

【参考方案6】:

您可以在基类中使用self.abstract_class = true 告诉 ActiveRecord 它是一个抽象类。

【讨论】:

以上是关于Rails STI - 防止基类实例化的主要内容,如果未能解决你的问题,请参考以下文章

如何在基类中实例化泛型类型?

如何在 JavaScript 中创建无法实例化的抽象基类

C ++使派生类只能从工厂/基类实例化

Typescript - 从基类中的静态方法实例化子类,使用 args 并引用其他静态方法

为啥我不能同时实例化对基类的引用作为派生类的指针?

为啥可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?