使用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请求解决,这个我在网上并没有找到相关内容。但是就目前来看,第一种方案已经很好的解决了我们的问题,如果有其他更好的方案可以解决这个问题大家可以留言讨论
参考: