golang包循环引用的几种解决方案总结

目录
  • 1. golang 包循环引用的几种解决方案
    • 1.1. 前言
    • 1.2. 新建公共接口包(父包), 将需要循环调用的函数或方法抽象为接口
    • 1.3. 新建公共组合包(子包), 在组合包中组合调用
    • 1.4. 全局存储需要相互依赖的函数, 通过关键字进行调用
    • 1.5. 不需要回调结果的可以通过事件总线 (eventBus) 解耦
  • 总结

1. golang 包循环引用的几种解决方案

1.1. 前言

golang 为了加速编译, 不允许包循环引用。通常来说, 只要你的包规划得好, 严格规范单向调用链 (如控制层 -> 业务层 ->数据层), 一般不会出现包循环引用问题。当然现实业务往往不会这么理想, 同层级之间的不同包经常需要互相引用, 下面我就分享几种解决包循环引用的方案。

1.2. 新建公共接口包(父包), 将需要循环调用的函数或方法抽象为接口

package_i

package package_i

type PackageAInterface interface {
	PrintA()
}

type PackageBInterface interface {
	PrintB()
}

package_a

package package_a

import (
	"cycle/package_i"
	"fmt"
)

type PackageA struct {
	B package_i.PackageBInterface
}

func (a PackageA) PrintA() {
	fmt.Println("I'm a!")
}

func (a PackageA) PrintAll() {
	a.PrintA()
	a.B.PrintB()
}

package_b

package package_b

import (
	"cycle/package_i"
	"fmt"
)

type PackageB struct {
	A package_i.PackageAInterface
}

func (b PackageB) PrintB() {
	fmt.Println("I'm b!")
}

func (b PackageB) PrintAll() {
	b.PrintB()
	b.A.PrintA()
}

main

package main

import (
	"cycle/package_a"
	"cycle/package_b"
)

func main() {
	a := new(package_a.PackageA)
	b := new(package_b.PackageB)
        a.B = b
        b.A = a
	a.PrintAll()
	b.PrintAll()
}

1.3. 新建公共组合包(子包), 在组合包中组合调用

package_c

package package_c

import (
	"cycle/package_a"
	"cycle/package_b"
)

type CombileAB struct {
	A *package_a.PackageA
	B *package_b.PackageB
}

func (c CombileAB) PrintAll() {
	c.A.PrintA()
	c.B.PrintB()
}

main

package main

import (
	"cycle/package_a"
	"cycle/package_b"
	"cycle/package_c"
)

func main() {
	a := new(package_a.PackageA)
	b := new(package_b.PackageB)
	c := new(package_c.CombileAB)
	c.A = a
	c.B = b
	c.PrintAll()
}

1.4. 全局存储需要相互依赖的函数, 通过关键字进行调用

callback_mgr

package callback_mgr

import (
	"fmt"
	"reflect"
)

var callBackMap map[string]interface{}

func init() {
	callBackMap = make(map[string]interface{})
}

func RegisterCallBack(key string, callBack interface{}) {
	callBackMap[key] = callBack
}

func CallBackFunc(key string, args ...interface{}) []interface{} {
	if callBack, ok := callBackMap[key]; ok {
		in := make([]reflect.Value, len(args))
		for i, arg := range args {
			in[i] = reflect.ValueOf(arg)
		}
		outList := reflect.ValueOf(callBack).Call(in)
		result := make([]interface{}, len(outList))
		for i, out := range outList {
			result[i] = out.Interface()
		}
		return result
	} else {
		panic(fmt.Errorf("callBack(%s) not found", key))
	}
}

package_a

package package_a

import (
	"cycle/callback_mgr"
	"fmt"
)

func init() {
	callback_mgr.RegisterCallBack("getA", new(PackageA).GetA)
}

type PackageA struct {
}

func (a PackageA) GetA() string {
	return "I'm a!"
}

func (a PackageA) PrintAll() {
	fmt.Println(a.GetA())
	fmt.Println(callback_mgr.CallBackFunc("getB")[0].(string))
}

package_b

package package_b

import (
	"cycle/callback_mgr"
	"fmt"
)

func init() {
	callback_mgr.RegisterCallBack("getB", new(PackageB).GetB)
}

type PackageB struct {
}

func (b PackageB) GetB() string {
	return "I'm b!"
}

func (b PackageB) PrintAll() {
	fmt.Println(b.GetB())
	fmt.Println(callback_mgr.CallBackFunc("getA")[0].(string))
}

main

package main

import (
	"cycle/package_a"
	"cycle/package_b"
)

func main() {
	a := new(package_a.PackageA)
	b := new(package_b.PackageB)
	a.PrintAll()
	b.PrintAll()
}

1.5. 不需要回调结果的可以通过事件总线 (eventBus) 解耦

eventBus

package eventBus

import (
	"github.com/asaskevich/EventBus"
)

var globalEventBus EventBus.Bus

func init() {
	globalEventBus = EventBus.New()
}

func Subscribe(topic string, fn interface{}) error {
	return globalEventBus.Subscribe(topic, fn)
}

func SubscribeAsync(topic string, fn interface{}, transactional bool) error {
	return globalEventBus.SubscribeAsync(topic, fn, transactional)
}

func Publish(topic string, args ...interface{}) {
	globalEventBus.Publish(topic, args...)
}

package_a

package package_a

import (
	"cycle/eventBus"
	"fmt"
)

func init() {
	eventBus.Subscribe("PrintA", new(PackageA).PrintA)
}

type PackageA struct {
}

func (a PackageA) PrintA() {
	fmt.Println("I'm a!")
}

func (a PackageA) PrintAll() {
	a.PrintA()
	eventBus.Publish("PrintB")
}

package_b

package package_b

import (
	"cycle/eventBus"
	"fmt"
)

func init() {
	eventBus.Subscribe("PrintB", new(PackageB).PrintB)
}

type PackageB struct {
}

func (b PackageB) PrintB() {
	fmt.Println("I'm b!")
}

func (b PackageB) PrintAll() {
	b.PrintB()
	eventBus.Publish("PrintA")
}

总结

到此这篇关于golang包循环引用的几种解决方案的文章就介绍到这了,更多相关golang包循环引用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 为什么GO不支持循环引用

    目录 1.案例演示 2.原因分析 3.总结 学习 Go 语言的开发者越来越多了,很多小伙伴在使用时,就会遇到种种不理解的问题. 其中一点就是包的循环引用的报错: package command-line-arguments imports github.com/eddycjy/awesome-project/a imports github.com/eddycjy/awesome-project/b imports github.com/eddycjy/awesome-project/a: im

  • golang包循环引用的几种解决方案总结

    目录 1. golang 包循环引用的几种解决方案 1.1. 前言 1.2. 新建公共接口包(父包), 将需要循环调用的函数或方法抽象为接口 1.3. 新建公共组合包(子包), 在组合包中组合调用 1.4. 全局存储需要相互依赖的函数, 通过关键字进行调用 1.5. 不需要回调结果的可以通过事件总线 (eventBus) 解耦 总结 1. golang 包循环引用的几种解决方案 1.1. 前言 golang 为了加速编译, 不允许包循环引用.通常来说, 只要你的包规划得好, 严格规范单向调用链

  • Golang实现字符串倒序的几种解决方案

    前言 本文主要给大家介绍了关于Golang实现字符串倒序的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 字符串倒置如下: Hello World --> dlroW olleH 解决方案1: length := len(str) array := make([]string , length) for i , v := range str{ array[i] = string(v) } for i := 0 ; i < length/2 ; i++ { array[

  • iOS NSTimer循环引用的几种解决办法

    发生场景 在 Controller B 中有一个 NSTimer @property (strong, nonatomic) NSTimer *timer; 你创建了它,并挂载到 main runloop self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:true]; 然后退出 Controller B 的

  • iOS如何巧妙解决NSTimer的循环引用详解

    一 发现问题 我们都知道NSTimer采用target-action的方式,通常target又是类本身,我们为了方便又把NSTimer声明为属性变量,这样就难免会造成循环引用(需要反复执行计时任务时,如果是单次的任务就不会造成循环引用). 例如: _timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(startTimer) userInfo:nil repeats:YES]; 深入理

  • JS关于for循环中使用setTimeout的四种解决方案

    目录 概述 解决方案1:闭包 解决方案2:拆分结构 解决方案3:let 解决方案4:setTimeout第三个参数 概述 我们先来简单了解一下setTimeout延时器的运行机制.setTimeout会先将回调函数放到等待队列中,等待区域内其他主程序执行完毕后,按时间顺序先进先出执行回调函数.本质上是作用域的问题. 因此若是这样将不会得到想要的结果输出1.2.3.4.5,而会连续输出5个6. for (var i=1; i<=5; i++) { setTimeout( function time

  • 解决IDEA中Maven依赖包导入失败报红问题(总结最有效8种解决方案)

    问题描述: 真的,说来话长,这应该是我花最多时间去解决关于Maven依赖包导入的问题,以前粘贴复制导入,自动下载成功了, 这次怎么搞,怎么让他自动下载都还是红红的一片, 花了大半天,各种尝试,只为搏得问题解决!!! 真的看着都难受, 但是, 终于, 还是让我搞定了,这次让我汇总所有最有可能解决这依赖问题的方法,下次遇到这种问题, 真的要说再见了 , 话不多开,开货!!!方案1:(本人平时遇到这问题的常规操作) 当一开始遇到导入的依赖报红了,直接把那段刚导入的对应依赖删了,然后又重新导入,它自动会

  • @OneToMany查询陷入循环引用的解决方案

    目录 @OneToMany查询陷入循环引用问题 问题报错如下 解决方案:按需改为LAZY即可 JPA@OneTOMany查询在ruturn的时候出现错误 java.lang.IllegalStateException原因和解决方法 具体的情况如下 解决 @OneToMany查询陷入循环引用问题 问题报错如下 主要信息为: wrapping Result set representing update count of 4> 以及: exception [Request processing fa

  • Spring循环依赖的三种方式(推荐)

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 Spring容器会将每一个正在创建的Bean 标识符放在一个"当前创建Bean池"中,Bean标识符在创建过程中将一直保持 在这个池中,因此如果在创建Bean过程中发现自己已经在"当前创建Bean池"里时将抛出 BeanCurrentlyInCrea

  • 浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyIn CreationException异常表示循环依赖. 如在创建TestA类时,构造器需要TestB类,那将去创建TestB,在创建TestB类时又发现需要TestC类,则又去创建Test

  • Python中循环引用(import)失败的解决方法

    前言 最近在开发智能家居项目hestia-rpi项目中,由于代码结构层级划分不合理,导致了循环引用(import)module失败的问题,错误如下: Traceback (most recent call last):   File "./main.py", line 8, in &lt;module&gt;     from hestiarpi.library.server import server   File "/home/pi/server/hest

随机推荐