子类的循环依赖

时间:2014-05-13 00:33:12

标签: ruby-on-rails ruby circular-dependency activesupport

假设我们使用的是Rails或ActiveSupport::Dependencies模块。现在让我们看一下这段代码:

animal.rb

class Animal
  CHILD = {
    cat: Cat
  }

  # factory!?
  def self.child(name)
    CHILD[name].new
  end
end

cat.rb

class Cat < Animal
end

dog.rb

class Dog < Animal
end

在加载所有类之前的某个地方:

Cat.new

该代码将生成B is not a class (TypeError)(activesupport 3.x)或Circular dependency detected while autoloading constant B(activesupport 4.x),因为它尚未创建,但已经在类中的类的名称表

要解决此问题,可以require 'a',然后A需要B

可以为我提供以下问题的更好解决方案吗?

2 个答案:

答案 0 :(得分:2)

出于这个原因,为了避免在Rails初始化或加载特定类时自动加载大量其他类,如果需要指定,Rails使用字符串来指定关联的类名,例如

has_many :published_posts, class_name: 'Post'

因为协会最容易发生这样的互动。

因此,如果您可以控制您使用的内容并且可以延迟加载常量,直到需要在Ruby / Rails中使用字符串来保存常量的名称,直到您需要对其进行常量化,它可能不是一个坏主意。但是,像任何事情一样,最好的解决方案取决于您的要求。

如果您来自Java之类的其他编程语言背景,这可能看起来很奇怪,至少在历史上,开发人员避免使用反射来动态地实例化String中的类,因为开销(在至少在旧版本中)以及在以后加载类时可能会出错,而不是在启动时尽早捕获它们。但是,这真的不是件坏事。而且这也不能阻止你在预先加载前的预先加载,因为它正在做的是消除在加载过程中由常量引用引起的直接依赖。

答案 1 :(得分:2)

如果您不想/不能使用require,则应避免循环依赖。避免它们的一种方法是用运行时级别的类替换类加载级别调用:

class Animal
  CHILD = {
    cat: 'Cat'
  }

  def self.child(name)
    CHILD[name].constantize.new
  end
end

确实现在无法在类级别调用self.child,或者您将再次处于循环依赖关系上。