如何解决Golang包循环引入问题?

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方法实现对不对!这个时候稍加对barfoo代码包修改即可!

修改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()
}

从上面修改可知PrintFooInfoPrintBarInfo方法现在接收一个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

那么循环引入问题也就通过接口完美解决了,循环引入的本质就是包与包相互引入,那接口就避免了这个问题,而是通过调用接口类型的值,只要一个类型实现了这个接口,就可以调用这个接口类型的值所定义的方法。

comments powered by Disqus