告别循环依赖:Wire 的三种优雅实现

Wire 并不会像 Java 的 Spring 一样,能自动处理循环依赖,Go 语言的设计哲学如此,不合理的特性,它就是不会支持。但开发业务时,难免有在同一层有循环依赖的情况。

方法一:将 func 传进去

在调用某方法时,直接将func传进去

go 复制代码
// a.go
type A struct {
}

// demo 方法
func (a *A) demo(bFn func())  {
    // 假设这里依赖B的方法
    // 则直接由上一层将需要的方法传进来
    bFn()
}

// b.go
type B struct {
}

// demo 方法
func (b *B) demo()  {
	// ...
}

方法二:依赖倒置

如果A依赖B,则在A中声明interface,在外层用 wire.Bind 将A中的interface与B的实现进行绑定

go 复制代码
// a.go
type A struct {
  // 直接写依赖
  b BIf
}

// 声明依赖的interface
type BIf interface {
	demo() int
}

// NewA 构造方法
func NewA(b BIf) *A {
  // 直接依赖B的interface
  return &A{b: b}
}

// demo 方法
func (a *A) demo()  {
    // 调用依赖的demo
    a.b.demo()
}

// b.go
type B struct {
}
func NewB() *B {
  return &B{}
}
// demo 方法
func (b *B) demo()  {
	// ...
}

// build.go
// 指定将B实例绑定到BIf上
wire.Build(
  NewA,
  NewB,
  // 将B实例绑定到BIf上
  wire.Bind(new(BIf), new(*B)), // wire.Bind 的第二个参数是类型,这里用 new(*B) 表示 *B 类型,并不是实例
))

方法三:使用Set*来实现

go 复制代码
// a.go
type A struct {
  // 依赖*B
  b *B
}


// NewA 构造方法
func NewA() *A {
  // 这里依赖的不能直接设置进去,暴露下面的SetA来
  return &A{}
}

// SetB 设置a依赖
func (a *A) SetB(b *B)  {
	a.b = b 
}

// demo 方法
func (a *A) demo()  {
    // 调用依赖的demo
    a.b.demo()
}

// b.go
type B struct {
  // 依赖*A
  a *A
}
func NewB() *B {
  return &B{}
}

// SetA 设置A依赖
func (b *B) SetA(a *A)  {
	b.a = a
}

// demo 方法
func (b *B) demo()  {
	// ...
}

// inject_dependencies.go
// InjectDependencies 是一个无业务含义的类型,仅用于在 wire 中触发 Set* 注入。
// 注意:它必须被其他 provider 引用(例如作为根组件的依赖,依赖一下不做任何调用),否则 wire 会报 unused provider 错误。
type InjectDependencies struct {
}

func NewInjectDependencies(a *A, b *B) *InjectDependencies {
	a.SetB(b) // 注入
	b.SetA(a) // 注入
	return &InjectDependencies{}
}


// build.go
wire.Build(
  NewA,
  NewB,
  // 提供依赖注入,用它来统一做各实例的Set*
  NewInjectDependencies,
)

对比

方案 推荐 原因
将 func 传进去 ⭐️⭐️⭐️ 如果一个实例多个方法都依赖,则每个方法都需要传,且阅读和跟踪不友好
依赖倒置 ⭐️⭐️⭐️⭐️ 工程思想上更合理,但同样存在阅读和跟踪代码困难问题
使用Set*来实现 ⭐️⭐️⭐️⭐️⭐️ 阅读和跟踪代码非常友好
相关推荐
liyinchi19881 天前
微信小程序支付遇到“由于小程序违规,支付功能暂时无法使用” 解决办法
java·微信小程序·go
天天喝旺仔1 天前
Go 泛型实战:从类型参数、约束到可复用泛型容器与函数
数据结构·算法·容器·go
gospace2 天前
【无标题】
go·gb28181·gowvp
我不会起名字3222 天前
一天一道算法题(35):电话号码的字母组合
java·数据结构·后端·python·leetcode·go·回溯
江湖十年2 天前
Go GOPROXY 兜底链的坑:从一次诡异的 CI lint 失败说起
后端·面试·go
秋风点枝3 天前
gogogo!go语言感悟大观--打卡day01
go·devops
探索云原生3 天前
KubeClipper 1.7.0 发布:Operation 优化与 Kubernetes 1.37 支持
linux·docker·云原生·kubernetes·go
右耳朵猫AI3 天前
Go周刊2026W37 | Ebitengine 纯 Go 化、simd 重写 TurboPFor、json/v2 落地
后端·微服务·go
福兮说3 天前
Gin 项目里的错误处理:让 Controller 不必知道错误是怎么来的
后端·go·gin·架构设计
福兮说3 天前
我们那个 IP 限流中间件,跑久了会把内存吃光
go