使用Listener机制解决Golang包环状引用

引言

这个问题出现在写项目中遇到的问题,即包的环状引用,引入Listener机制可以解决这个问题,其实这个解决方案也可以扩展至其他语言。

解决

其实问题的描述很容易,就是两个包之间互有引用,当然最好 的解决方案就是在设计的时候可以考虑到这个问题,从而在设计阶段就避免这个问题,但不是每个人都可以在设计阶段看的那么深的,所以在出现这个问题的时候我们需要一种机制解决这个问题,其实就是一种特殊的回调机制。

package A
testA.go
func LoadServerConfig(filename string, cfg *Connect.ServerConfig) bool{
....
}

func init(){
	Connect.RegisterRestServerListener(LoadServerConfig)
}
package B
testB.go

type ServerConfig struct{
...
}

type ServerListener func(filename string, cfg *ServerConfig) bool

var ServerListeners []ServerListener

func RegisterRestServerListener(l ServerListener) {
	ServerListeners = append(ServerListeners, l)
}

我们可以清楚的看到A包中包含了B包中的ServerConfig,B包中又要使用LoadServerConfig,此时就会出现环状引用,解决方案就是引入一个ServerListeners,在A包的init函数中把LoadServerConfig当做一个回调调用RegisterRestServerListener放入ServerListeners,此时B包中就不必直接调用B包中的LoadServerConfig了,直接调用ServerListeners中的回调就OK了,但是在需要调用B包的地方我们也要引入A包,并在import后面加上_,代表执行包中的init函数,这样才可以把A包的函数挂到B包。

其实还有一种方法,就是[1]中的方法,但是我个人认为其实都差不多,没有太搞懂其中指针转来转去是什么意思,以为写了一个简单的测试代码可以看出转不转都是可以跑通的。

package test_cycle_a

import (
"fmt"
"hello/test_cycle_b"
)

func init() {
	str := "hello world"
	test_cycle_b.Test(str, rocketfunction)
}

func rocketfunction(str string)  {
	fmt.Println("hello : " ,str)
}
package test_cycle_b

import (
	"fmt"
	"strconv"
	"unsafe"
)

type Callback func(str string)

var Fun Callback

func Test(str string, callback Callback)  {
	//pointer 转 string
	straddress := &callback
	strPiniter := fmt.Sprintf("%d", unsafe.Pointer(straddress))
	fmt.Println("connection is", strPiniter)

	//string 转 pointer
	intPointer, _ := strconv.ParseInt(strPiniter, 10, 0)
	var pointer *Callback
	pointer = *(**Callback)(unsafe.Pointer(&intPointer))

	(Callback)(*pointer)(str)
	Fun = (Callback)(*pointer)
}
package main

import "hello/test_cycle_b"
import _ "hello/test_cycle_a"


func main(){
	test_cycle_b.Fun("lizhaolong")
}

ouput:
connection is 824634925080
hello :  hello world
hello :  lizhaolong

倘若我们在package test_cycle_b把Test函数换成如下函数:

func Test(str string, callback Callback)  {
	Fun = callback
	return
}

继续调用上面的main函数,我们发现仍旧是可以成功运行的,其实此时就和第一种解决方案一样了。

其实在[1]中还提到了使用HTTP请求解决,这个我在网上并没有找到相关内容。但是就目前来看,第一种方案已经很好的解决了我们的问题,如果有其他更好的方案可以解决这个问题大家可以留言讨论

参考:

  1. 博文《golang解决import cycle not allowed的一种思路
  2. 博文《bugfan/mytools
posted @ 2022-07-02 13:16  李兆龙的博客  阅读(42)  评论(0编辑  收藏  举报