一、go中为什么不允许循环依赖
二、如何解决循环依赖
循环依赖就是A引用B,B又引用A,形成了一个包引用的闭环。要解决循环引用,就是打破这个闭环,让A引用B,B不能引用A。看下面的例子:

包结构如下:


FC1693D4-7086-4169-923F-58BD068703B4.png
package bagA

import (
    "fmt"
    "gotest/test/go_basic/problem_record/bag_circle_import/bagB"
)
func GetName()string{
    return "bag A"
}

func PrintA(){
    fmt.Println(fmt.Sprintf("I am bag A, and %s is bag B", bagB.GetName()))
}
package bagB

import (
    "fmt"
)

func GetName()string{
    return "bag B"
}

func PrintB(){
    fmt.Println(fmt.Sprintf("I am bag B, and %s is bag A", AHandler()))
}
package main

import "gotest/test/go_basic/problem_record/bag_circle_import/bagA"

func main(){
    bagA.PrintA()
}

执行main函数报错:


image.png

报错的原因是 我们在执行bagA.PrintA()的时候,引用了A包,A包又引用了B包,B包又引用了A包,形成了循环依赖。那我们打破依赖就可以了。

那么该怎么打破呢?

我们发现A包引用B包,是因为A包需要调用B包的bagB.GetName()方法;同样的,B包引用A包,是因为B包需要调用A包的bagA.GetName()方法。那么,我们有没有不需要引包就能使B包可以调用A包的方法呢?

当然是有的。看下面:

package bagA

import (
    "fmt"
    "gotest/test/go_basic/problem_record/bag_circle_import/bagB"
)

func init(){
    bagB.Register(GetName)
}

func GetName()string{
    return "bag A"
}

func PrintA(){
    fmt.Println(fmt.Sprintf("I am bag A, and %s is bag B", bagB.GetName()))
}
package bagB

import (
    "fmt"
)

func GetName()string{
    return "bag B"
}

func PrintB(){
    fmt.Println(fmt.Sprintf("I am bag B, and %s is bag A", AHandler()))
}

var AHandler func()string

func Register(f func()string){
    AHandler = f
}

我们在B包里定义了一个方法变量AHandler,并且提供了为这个方法变量赋值的方法Register(),然后在A包里的init()方法里,调用B包的Register()方法,将A包的GetName方法复赋值给了AHandler变量。 这样,在B包执行方法AHandler是不是就相当于调用了A包的GetName方法呢?看执行结果:


image.png

总结:
上述解决办法的核心逻辑就是,B包使用一个方法变量来替代A中的方法(来完成B不引用A),A来为该变量赋值(因为A引用B,A可以调用B的方法来完成赋值)。 解决循环依赖问题,思想就是打破包的循环依赖,以不导包的方式调用其他包的方法。所以,采用接口的形式也可以解决循环依赖(B定义一个接口,A中你想要调用的方法实现了该接口,A中完成接口变量赋值,B来调用接口方法,有时间再补充例子吧)