关于如何避免Go中的导入周期的任何好建议?

时间:2013-04-23 11:49:49

标签: dependencies go circular-dependency

我在Go项目上工作了一个月。好的是Go非常高效。但经过一个月的开发,我已经有了数千行代码和许多packages。为了避免导入周期对我来说是一个主要问题,即每当我遇到导入周期错误时,我都不知道第一次出现问题的位置。

Go编译器也只有非常简单的通知,总是不足以快速定位问题,如:main.go:7:3: import cycle not allowed。它只会帮助您了解哪个文件可能导致问题,但不会更深入。由于import关系在代码增长时变得越来越复杂,我迫切希望知道如何在Go中更有效地避免导入周期。非常感谢任何帮助。

4 个答案:

答案 0 :(得分:57)

go list -f '{{join .Deps "\n"}}' <import-path>

如果<import-path>为空,则会在<import-path> - 或当前目录中显示包的导入依赖关系。可替代地

go list -f '{{join .DepsErrors "\n"}}' <import-path>

希望在您的案例中显示一些有用的信息。另见

的输出
go help list

有关go list工具的其他信息。

答案 1 :(得分:41)

为了补充jnml的答案(这有助于“调试”循环引用问题),你可以使用dependency inversion打破这些循环,再加上依赖注入。对于一个应用程序,我总是尝试遵循Clean Architecture的指导 - 请参阅here以获取特定于Go的示例 - 我发现Go的“非声明性实现”接口(即,您不必明确地说type MyStruct struct implements IfceSomething)使这很简单。

因此,如果你有软件包A -> B -> C -> A,你可以在软件包C中创建InterfaceA(一些相关名称,显然,与包相关的行为相关:),并使其依赖于此接口而不是在包A上,你确保包A“实现”这个接口。

然后你必须在某个时候提供A到C的具体实现(这里有很多可能性,我通常在知道所有依赖关系的主包中执行这个“粘合”代码)。

答案 2 :(得分:7)

  

由于导入关系在代码中变得越来越复杂   我越来越渴望知道如何更有效地避免进口周期   去。

另一种选择是可视化项目中的依赖项。这可以使用CLI工具godepgraph完成。 您可以使用以下命令安装它:

go get -u github.com/kisielk/godepgraph

然后使用它在另一个CLI工具graphvis的帮助下在您的应用中查找导入周期。 使用此工具可以可视化包依赖关系:

godepgraph -s path/to/my/package | dot -Tpng -o godepgraph.png
open ./godepgraph.png

在我的代码中查找周期: enter image description here

答案 3 :(得分:0)

作为使用 go list 工具的旁白,它可能会确认您已经知道的依赖关系,从架构的角度来看,您希望确保您的依赖关系树足够深,以便您可以通过构建子项来找出循环组件。如果模块中存在导入循环,那么应该清楚存在的循环。应该有足够的模块化(树深度)来无缝地移动这些依赖项。

Model -> Field  (Uses A) -- Needs to import "System"
Model -> System (Defines A) -- But needs to import "Field"
----------Move type A Struct A.go to top of module----------
----------This is what the Model Dir looks like now---------
Model -> A
Model -> Field
Model -> System

现在依赖已经分离了,孩子们可以自由使用A了。这可能无助于可视化依赖关系或成为一个很好的基于工具的解决方案,但同样,如果你足够解耦你的逻辑并构建子组件,你应该很快收敛到循环上。否则,如果您使用的是树形可视化工具,我会说这是最后的手段,而且是设计不佳/子组件不足的结果。