假设我们使用的是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
。
可以为我提供以下问题的更好解决方案吗?
答案 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
,或者您将再次处于循环依赖关系上。