Go不允许存在包的循环引入,你是不是也在项目开发时遇到过 import cycle not allowed
编译错误呢? 你找到好的解决方案了没有呢? 下文也许可能会对你解决这个问题有所帮助~
何为包的循环引入
如在项目中有bar
包和foo
包,假如bar包用到了foo包代码,并且foo包也用到了bar包代码,那么这时就会产生编译错误import cycle not allowed
意为"不允许循环引入"。
假定示例项目结构如下:
1
2
3
4
5
6
7
8
9
|
$ depend git:(master) tree
.
├── bar
│ └── bar.go
├── foo
│ └── foo.go
└── main.go
2 directories, 3 files
|
bar/bar.go代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package bar
import (
"depend/foo"
"fmt"
)
type Bar struct {
Name string
Description string
}
func NewBar(name string, description string) *Bar {
return &Bar{Name: name, Description: description}
}
func (b *Bar) Info() {
fmt.Printf("name is %s, description is: %s\n", b.Name, b.Description)
}
func (b *Bar) PrintFooInfo() {
f := foo.NewFoo("Foo", "Foo is struct object")
f.Info()
}
|
foo/foo.go代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package foo
import (
"depend/bar"
"fmt"
)
type Foo struct {
Name string
Description string
}
func NewFoo(name string, description string) *Foo {
return &Foo{Name: name, Description: description}
}
func (f *Foo) Info() {
fmt.Printf("name is %s, description is: %s\n", f.Name, f.Description)
}
func (f *Foo)PrintBarInfo() {
b := bar.NewBar("Bar", "Bar is struct object")
b.Info()
}
|
main.go 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import (
"depend/bar"
"depend/foo"
)
func main() {
b := bar.NewBar("Bar", "Bar is struct object")
b.PrintFooInfo()
f := foo.NewFoo("Foo", "Foo is struct object")
f.PrintBarInfo()
}
|
那么这个时候你运行main函数会得到如下编译错误提示信息:
1
2
3
4
5
|
import cycle not allowed
package depend
imports depend/bar
imports depend/foo
imports depend/bar
|
如何解决包的循环引入
使用接口解决,网上有也博文说明使用分包的解决方案但是个人觉得不太理想,这是只说一下使用接口解决循环引入问题。
使用接口解决
在项目目录下新建一个inter(inter/inter.go)代码包并定义一个接口:
1
2
3
4
5
|
package inter
type PrintInfo interface {
Info()
}
|
那么这个时候是不是 bar
包和 foo
包是不是已经实现了这个PrintInfo
接口,因为各包都有Info方法实现对不对!这个时候稍加对bar
和foo
代码包修改即可!
修改bar/bar.go代码包方法PrintFooInfo
如下:
1
2
3
|
func (b *Bar) PrintFooInfo(pi inter.PrintInfo) {
pi.Info()
}
|
修改foo/foo.go代码包方法PrintBarInfo
如下:
1
2
3
|
func (f *Foo)PrintBarInfo(pi inter.PrintInfo) {
pi.Info()
}
|
从上面修改可知PrintFooInfo
和PrintBarInfo
方法现在接收一个inter.PrintInfo
接口类型的值,那好了,在main函数调用时直接将要调用的接口类型值传递进去,这个问题也解决掉了。
修改main.go主函数main如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import (
"depend/bar"
"depend/foo"
)
func main() {
b := bar.NewBar("Bar", "Bar is struct object")
f := foo.NewFoo("Foo", "Foo is struct object")
b.PrintFooInfo(f)
f.PrintBarInfo(b)
}
|
现在运行main.go得到如下输出:
1
2
|
name is Foo, description is: Foo is struct object
name is Bar, description is: Bar is struct object
|
那么循环引入问题也就通过接口完美解决了,循环引入的本质就是包与包相互引入,那接口就避免了这个问题,而是通过调用接口类型的值,只要一个类型实现了这个接口,就可以调用这个接口类型的值所定义的方法。