说我在golang中有这两个文件:
// main/a/a.go
import "main/b"
type Model struct {
ID int `json:"id"`
Me int `json:"me"`
You int `json:"you"`
}
func zoom(v b.Injection){
}
func Start(){
// ...
}
然后第二个文件如下:
// main/b/b.go
import "main/a"
type Injection struct {
ModelA a.Model
}
func GetInjection() Injection {
return Injection{
ModelA: a.Start(),
}
}
如您所见,这些是循环导入,每个文件都导入另一个。 因此,我需要使用第3个文件,并让这两个文件导入第3个文件。
但是我真的在努力获得此功能并避免循环导入。
我的第一步是将Injection
类型移动到第3个文件中:
// main/c/c.go
type Injection struct {
ModelA interface{} // formerly a.Model
}
所以现在是这样:
a imports c
b imports a,c
因此不再有循环,但是问题是我不知道如何在a.Model
中为c.go
创建接口?出于正常原因,我上面使用的空interface{}
无效。
如何用这两个原始文件解决此循环导入问题?
答案 0 :(得分:2)
如果要将它们放入单独的包装中,则不能在同一包装中包含Model
和zoom()
,因为zoom()
指的是Injection
和{{ 1}}是指Injection
。
因此,可能的解决方案是将Model
放入软件包Model
,将a
放入软件包zoom()
,并将b
放入软件包Injection
。 c
可以引用c.Injection
,a.Model
可以引用b.zoom()
。里面没有圆圈:
c.Injection
我假设您的真实代码中还有其他引用可能会阻止此引用的工作,但您可以在软件包之间移动“填充”,也可以将其分解为更多。
此外,如果事物紧密结合在一起,那么您应该真正考虑将它们放入同一包装中,这样就没有问题可以解决。
解决循环导入问题的另一种方法是引入接口。例如。如果您的 b.zoom() --------> c.Injection ---------> a.Model
函数不会引用zoom()
,则包含Injection
和Model
的程序包将不需要引用zoom()
的程序包。
检查Injection
与zoom()
有什么关系。如果那是方法调用,那已经很好。如果没有,请向Injection
添加方法。然后,您可以在Injection
的程序包中定义一个接口,其中包含需要调用的方法zoom()
,并将其参数类型更改为此接口。在Go中实现接口是隐式的,没有意图的声明。因此,您可以删除参数类型中的引用,仍然可以将zoom()
的值传递给Injection
。
也相关,请检查Dave Cheney's thoughts about organizing code:
我认为代码应该按照软件包提供的内容(而不是包含的内容)组织到软件包名称中。这有时可能很微妙,但通常不是。
例如,http提供http客户端和服务器。
作为反例,软件包utils是一个不好的名字,是的,它提供了实用程序,但您不知道名称是什么,实际上,此软件包是根据其包含的内容来命名的。
如果您的项目是一个库,则它应该包含一个软件包(不包括示例和可能的实用程序命令),如果它包含更多的软件包,则表明该库正试图做太多事情。
默认情况下,最好避免使用多个程序包,只有在有明确的关注点分离时,才按程序包拆分代码。以我的经验,复杂和可能是圆形包装结构的许多挫败是项目中包装太多的结果。