44

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

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

4

4 回答 4

58
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 工具的更多信息。

于 2013-04-23T12:15:11.907 回答
44

为了补充 jnml 的答案(这有助于“调试”循环引用问题),您可以使用依赖反转来打破这些循环,再加上依赖注入。对于应用程序,我总是尝试遵循Clean Architecture的指导方针——请参阅此处以获取特定于 Go 的示例——并且我发现 Go 的接口的“非声明性实现”(也就是说,您不必明确地说type MyStruct struct implements IfceSomething) 使这非常简单。

所以,如果你有包A -> B -> C -> A,你在包 C 中创建InterfaceA(一些相关的名称,显然,比包相关的行为更相关:) 并使其依赖于这个接口而不是包 A,并且你确保包 A“实现"这个界面。

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

于 2013-04-23T17:27:18.497 回答
10

由于随着代码的增长,导入关系变得越来越复杂,我很想知道如何在 Go 中更有效地避免导入循环。

另一种选择是可视化项目中的依赖关系。这可以使用 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

在我的代码中查找循环: 在此处输入图像描述

于 2018-03-26T17:34:44.743 回答
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了。这可能无助于可视化依赖关系或成为一个很好的基于工具的解决方案,但再一次,如果你足够解耦你的逻辑并构建子组件,你应该很快收敛到循环中。否则,如果您使用的是树可视化器,我会说这是最后的手段,并且是设计不佳/子组件不足的结果。

于 2021-01-03T20:03:54.730 回答