我意识到以前已经回答了有关循环依赖的问题,但是,答案通常只是说合并包。长话短说,我有一个许多类型将实现的接口。我想有一种方法来选择在运行时使用其名称使用这些类型中的哪一种。这也将用于序列化。我以类的名称读取,然后实例化正确的名称。
我使用了策略模式。包A
中有一个Base
接口。
package A
import (
"../C"
)
type Base interface {
doStuff(p C.Profile) int
}
type Operation struct {
Base Base
Data int
}
func (o *Operation) execute(p C.Profile) int {
return o.Base.doStuff(p)
}
然后,有一些类型在包B
中实现该接口。
//file Impl1.go
package B
import (
"../C"
)
type Impl1 struct {}
func (b *Impl1 ) doStuff(p C.Profile) int {
...
}
//file Impl2.go
package B
import (
"../C"
)
type Impl2 struct {}
func (b *Impl2 ) doStuff(p C.Profile) int {
...
}
然后在包C
中,我有一个结构Foo
,字段类型为Base
.此字段可以指向包B
中的任何实现。我们可以在运行时选择实现。这是我最终想要序列化的结构。
package C
import (
"../A"
"../B"
)
type Foo struct {
bar A.Base
baz []Profile
...
}
func (f *Foo) changeBar(name string, data int) {
switch name {
case "Impl1":
f.bar = Operation{Base: B.Impl1{}, Data: data}
case "Impl2":
f.bar = Operation{Base: B.Impl2{}, Data: data}
...
}
编辑:同样在C
包中,我们有Profile
,这就是A
和B
包需要导入它的原因。
此代码具有循环依赖关系C
->B
->C
。一个明显的解决方案是将Profile
移动到不同的包。但这是不可能的,因为Profile
(以及C
包中的许多其他类似类型(和Foo
非常紧密耦合并且属于同一包(在这个最小的工作示例中可能不是那么明显(。这是这类问题的其他答案所暗示的,但我想学习如何使用它,使用我从使用其他语言中学到的良好实践。
另一种解决方案是以某种方式将工厂方法changeBar
移动到另一个包,并且仅在C
包的外部使用它(从而避免循环(,将其结果作为参数传递给C
,但在某些情况下(特别是序列化(我实际上需要在C
包的内部使用它。
我花了很多时间来解决这个问题,但我最终得到的要么将所有内容放在一个巨大的包中,要么将所有文件都放在一个单独的包中并导出所有内容。在其他编程语言中,有可能具有这些循环依赖关系或一次从包中导入一个"类"。围棋的路是什么?
您可以使用类型注册表中断从 C 到 B 的依赖关系:
package C
import "A"
var TypeRegistry = map[string]func() A.Base {}
func (f *Foo) changeBar(name string, data int) {
if factory, ok:=TypeRegistry[name]; ok {
f.bar=Operation{Base:factory(),Data:data}
}
}
然后在任何包中注册您的实现:
package B
import "../C"
func init() {
C.TypeRegistry["myType"]=func() A.Base { return MyType{}}
}