我想创建一个模块系统,您可以在其中以循环方式加载依赖项。原因是,在模型类之间的关系中,循环依赖无处不在,就像在 Ruby on Rails 中一样:
# app/models/x.rb
class X < ActiveRecord::Base
has_many :y
end
# app/models/y.rb
class Y < ActiveRecord::Base
belongs_to :x
end
这通常在 Ruby on Rails 模型中的工作方式,至少,它首先加载所有模型,依赖项首先是一个符号或“字符串”。然后一个集中管理器获取所有这些对象并将字符串转换为相应的类,现在这些类都已定义。当你设置第一个类与其对应的类的关系时,其余的类仍然与字符串有关,而这个第一个类与类有关,因此有一个过渡期,其中一些模型完全解析为类,而其他的仍然是字符串,因为算法正在运行并且尚未完成。一旦所有的字符串都被解析为它们对应的类,算法就完成了。
我可以想象一个变得更加复杂的世界,创建的循环不是一对一的循环依赖,而是在它们之间有很多层,创建一个 5 节点循环之类的东西。甚至是模块之间存在来回依赖关系的地方,例如:
# file/1.rb
class X
depends_on A
depends_on B
end
class Y
depends_on B
end
class Z
depends_on Q
end
# file/2.rb
class A
depends_on Y
end
class B
depends_on Z
end
# file/3.rb
class Q
depends_on X
end
要解决此问题:
- 加载所有文件 *.rb,将
depends_on
其视为字符串。现在我们在其相应的模块中定义了每个类,但使用depends_on
as 字符串而不是所需的类。 - 遍历文件并解析类。
- 对于文件 1.rb,解析 X... 等。
- X 依赖于 A,因此与 2/A 相关联。
- X 也依赖于 B,因此与 2/B 相关联。
- Y 取决于 B,因此与 2/B 相关联。
- Z 取决于 Q,因此与 3/Q 相关联。
- A 取决于 Y,因此与 1/Y 相关联。
- B 取决于 Z,因此与 1/Z 相关联。
- Q 取决于 X,因此与 1/X 相关联。
- 完毕。
所以基本上,有两个“回合”。第一轮是加载所有文件,并初始化类。第二轮是将班级成员与相应的其他班级相关联。顺序无所谓。
但它可以变得更复杂吗?需要两轮以上来解决这种循环依赖?我不确定。以这样的为例:
# file/1.rb
class A < A_P
class AA < A_P::AA_P
class AAA < A_P::AA_P::AAA_P
end
end
end
class B < B_Q
class BB < B_Q::BB_Q
class BBB < B_Q::BB_Q::BBB_Q
end
end
end
# file/2.rb
class A_P
class AA_P < B
class AAA_P < B::BB
end
end
end
class B_Q
class BB_Q < A
class BBB_Q < A::AA
end
end
end
在这种人为的情况下,您有:
A
(file/1) 取决于A_P
(file/2),然后:AA
(file/1) 取决于A_P
和AA_P
,然后:AA_P
(file/2) 取决于B
(file/1),然后:B
(file/1) 取决于B_Q
(file/2) 等....
也就是说,似乎正在发生一些奇怪的事情。我不确定,我的头开始打结。
- 在完全解决类之前,您无法定义
A
正在扩展的类。在类完全解析之前,A_P
您无法定义什么是类,这取决于被解析,这取决于被解析。ETC..AA
AA_P
B
B_Q
是否有可能解决这种循环依赖?解决任意复杂循环依赖的一般算法是什么?这样,最终是所有循环依赖项都与实际值相关联,而不是字符串或其他表示实际值的符号。解决循环依赖的最终结果是每个引用都应该引用实际的对象。
它总是只是一个简单的两遍算法,首先加载基础对象,然后解析它们的依赖关系,将依赖关系的“字符串”转换为集合中的基础对象?
你能举出一个例子,它需要的不仅仅是简单的两遍算法吗?然后描述在这种情况下算法应该如何解决循环依赖?或者证明/解释如何确定只需要一个简单的两遍算法?
另一个例子可能是:
// ./p.jslike
import { method_x } from './q'
import { method_y } from './q'
function method_a() {
method_x()
}
function method_b() {
console.log('b')
}
function method_c() {
method_y()
}
function method_d() {
console.log('d')
}
// ./q.jslike
import { method_b } from './p'
import { method_d } from './p'
function method_x() {
method_b()
}
function method_y() {
method_b()
}
我想这也将是两次通过。