前言

go项目运行报错: import cycle not allowed,导入循环(import cycle)

报错原因,在Go语言中,两个或更多的包之间形成了相互依赖的关系,即A包导入了B包,而B包又反过来导入了A包,形成一个循环。

这种循环会导致编译器无法确定每个包的完整依赖图,因为它们互相引用,就像一个无解的链条。

避免导入循环的关键在于理解包的依赖结构。一般来说,应该尽量保持单向依赖,也就是说,一个包不应该直接或间接地导入其自身,也不应形成一个闭合的循环。

在代码中,可以通过接口类型的变量来使用这些包,而不是直接使用具体的包。

这样你的代码不再依赖于具体的包,而是依赖于接口。

举例一

假设有两个包 pkgApkgB,它们需要相互调用对方的功能。我们将通过引入接口来避免循环依赖。

代码示例

文件结构

/project
├── main.go
├── pkgA
│ └── pkgA.go
└── pkgB
└── pkgB.go

pkgA.go

// pkgA/pkgA.go
package pkgA // 定义一个接口
type BInterface interface {
PerformB()
} // 包A的一个功能
func PerformA(b BInterface) {
// 调用包B的功能
b.PerformB()
}

pkgB.go

// pkgB/pkgB.go
package pkgB import "example/pkgA" // 实现包A定义的接口
type StructB struct{} // 实现PerformB方法
func (b StructB) PerformB() {
// 包B的具体实现
println("Performing B")
} // 包B的一个功能
func CallA() {
// 使用包A的功能
pkgA.PerformA(StructB{})
}

main.go

// main.go
package main import "example/pkgB" func main() {
// 调用包B的功能
pkgB.CallA()
}

代码解析

  1. 定义接口:在 pkgA 中定义一个接口 BInterface,该接口包含一个方法 PerformB()。这个接口的目的是让 pkgA 不需要直接依赖 pkgB,而是依赖于接口。

  2. 实现接口:在 pkgB 中定义一个结构体 StructB,并实现 PerformB() 方法。这样,pkgB 可以在需要调用 pkgA 的功能时,通过接口传递自身的实例。

  3. 使用功能:在 pkgBCallA() 方法中,调用 pkgAPerformA() 方法,并传入 StructB 的实例。这样,pkgA 就可以通过接口调用 pkgB 的功能,而不需要直接依赖于 pkgB

  4. 主函数:在 main.go 中,调用 pkgBCallA() 方法,启动整个流程。

运行结果

当运行 main.go 时,输出将会是:

Performing B

通过引入接口 BInterface,我们成功地解耦了 pkgApkgB,避免了循环依赖的问题。这种设计使得代码更加灵活和可维护。

举例二

模拟一个简单的支付系统,其中有两个包:paymentnotification

场景描述

  • payment 包负责处理支付逻辑。
  • notification 包负责发送通知(例如,支付成功的通知)。

我们将通过接口来解耦这两个包,以避免循环依赖。

文件结构

/project
├── main.go
├── payment
│ └── payment.go
└── notification
└── notification.go

payment.go

// payment/payment.go
package payment import "example/notification" // 定义一个接口
type Notifier interface {
SendNotification(message string)
} // 支付处理函数
func ProcessPayment(amount float64, notifier Notifier) {
// 处理支付逻辑(简化)
if amount > 0 {
// 支付成功,发送通知
notifier.SendNotification("Payment of $" + fmt.Sprintf("%.2f", amount) + " was successful.")
}
}

notification.go

// notification/notification.go
package notification import "fmt" // 实现包 payment 的 Notifier 接口
type EmailNotifier struct{} // 实现 SendNotification 方法
func (e EmailNotifier) SendNotification(message string) {
fmt.Println("Email Notification:", message)
} // 实现一个其他的通知方式(例如 SMS)
type SMSNotifier struct{} // 实现 SendNotification 方法
func (s SMSNotifier) SendNotification(message string) {
fmt.Println("SMS Notification:", message)
}

main.go

// main.go
package main import (
"example/notification"
"example/payment"
) func main() {
emailNotifier := notification.EmailNotifier{}
smsNotifier := notification.SMSNotifier{} // 使用 EmailNotifier 发送通知
payment.ProcessPayment(100.0, emailNotifier) // 使用 SMSNotifier 发送通知
payment.ProcessPayment(50.0, smsNotifier)
}

代码解析

  1. 定义接口:在 payment 包中,定义了一个接口 Notifier,包含一个方法 SendNotification(message string)。这个接口的目的是让 payment 包不直接依赖于 notification 包,而是依赖于一个抽象的通知接口。
  2. 支付处理:在 payment 包中,ProcessPayment 函数负责处理支付逻辑,并在支付成功时调用 notifier.SendNotification() 方法。这里的 notifier 参数是 Notifier 接口的实现。
  3. 实现通知:在 notification 包中,定义了两个结构体 EmailNotifierSMSNotifier,它们都实现了 Notifier 接口的 SendNotification 方法。
  4. 主函数:在 main.go 中,我们创建了 EmailNotifierSMSNotifier 的实例,并将它们传递给 ProcessPayment 函数。这样,支付处理逻辑可以根据不同的实现发送通知。

运行结果

当运行 main.go 时,输出将会是:

Email Notification: Payment of $100.00 was successful.
SMS Notification: Payment of $50.00 was successful.

通过引入接口 Notifier,成功地解耦了 paymentnotification 包。这样,payment 包不需要知道 notification 包的具体实现,只需要依赖于接口。这种设计使得系统更加灵活,便于扩展和维护。

go 定义接口解决 import cycle not allowed的更多相关文章

  1. go 报错 import cycle not allowed

    运行时报错,import cycle not allowed : 查了goole大概知道了原因,还是导包类的问题,我检察了一下我的代码库,发现我昨天划分几个工具文件,里面的两个文件相互引用,就导致报i ...

  2. import cycle not allowed in test

    写个 sdk 的测试时报错 import cycle not allowed in test 后发现因为测试文件内多写了导入同包路径. 同 package 下的 xxx_test.go 内不需要额外 ...

  3. linux系统:go build报错import cycle not allowed

    go build 困扰我多时的 go 编译报错:循环导入,代码肯定是没问题的,网上查说重新安装go 我觉得也不是太好的办法 import cycle not allowed package day01 ...

  4. go环境import cycle not allowed问题处理

    1.前言 今天在搭建Go语言环境,从https://golang.org/dl/上下载一个Go语言环境.环境变量配置刚开始如下配置: GOROOT=/home/go/bin     go执行文件目录 ...

  5. python定义接口继承类invalid syntax解决办法

    class s_all(metaclass=abc.ABCMeta): #python2.7用此方法定义接口继承 # __metaclass__ = abc.ABCMeta @abc.abstract ...

  6. python定义接口继承类

    zxq547 python定义接口继承类invalid syntax解决办法 1 2 3 4 5 6 7 class s_all(metaclass=abc.ABCMeta):     #python ...

  7. struts2 在拦截器进行注入(依据Action是否实现自己定义接口)

    比如:经常在Action中都须要获取当前登录的User,就须要获取Session.然后从Session获取当前登录的User,由于这些步骤都是反复操作,能够想办法在拦截器中进行实现.能够自己定义一个接 ...

  8. 用关键字interface定义接口,通过关键字implements来实现接口

    [定义]Java中,能够完成特定功能的,由若干属性和方法组织成的,相对独立的属性和方法的集合. [用途]实现类的多继承,以解决Java只能单继承,不支持多继承的问题. [特点] 用关键字interfa ...

  9. go定义接口以及类怎么使用接口

    go定义接口以及类怎么使用接口 多态是指代码可以根据类型的具体实现采取不同行为的能力.如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值. 接口是用来定义行为的类型.这些被定义 ...

  10. JAVA定义接口格式:

    [public]interface 接口名称 [extends父接口名列表] { //静态常量 [public] [static] [final] 数据类型变量名=常量值; //抽象方法 [publi ...

随机推荐

  1. Eureka 缓存机制详细配置

    https://blog.csdn.net/qwe86314/article/details/94963865 上节为大家介绍了 Eureka 的工作原理,其中提到了 Eureka Server 内部 ...

  2. 基于开源IM即时通讯框架MobileIMSDK:RainbowChat v8.3版已发布

    关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级.高度提炼,一套API优雅支持UDP .TCP .WebSocket 三种协议,支持iOS.A ...

  3. WxPython跨平台开发框架之模块字段权限的管理

    在我的很多Winform开发项目中,统一采用了权限管理模块来进行各种权限的控制,包括常规的功能权限(工具栏.按钮.菜单权限),另外还可以进行字段级别的字段权限控制,字段权限是我们在一些对权限要求比较严 ...

  4. Solution Set - 冬日纪行(前半)

    目录 0.「集训队互测 2018」「LOJ #2504」小 H 爱染色 ✡️ 1.「LOJ #6402」yww 与校门外的树 2.「集训队互测 2019」「LOJ #3075」组合数求和 ✡️ 3.「 ...

  5. .NET 9 new features-Microsoft.ML.Tokenizers 库

    在 .NET 9 中,微软引入了 Microsoft.ML.Tokenizers 库,为 .NET 开发者提供了强大的文本标记化功能. 一.什么是Microsoft.ML.Tokenizers Mic ...

  6. biancheng-Spring MVC-HandlerMapping

    一.HandlerMapping 作用是根据当前请求的找到对应的 Handler,并将 Handler(执行程序)与一堆 HandlerInterceptor(拦截器)封装到 HandlerExecu ...

  7. C#遍历获取文件夹下所有文件

    1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using ...

  8. nvim及插件安装配置

    1. install neovim 1 sudo apt install neovim After installing neovim, we can delete old vi. 3. instal ...

  9. 基于C#实现多线程启动停止暂停继续

    大家好!我是付工. 大部分初学者在学习C#上位机编程时,多线程是一个很难逾越的鸿沟,不合理地使用多线程,会导致经常出现各种奇怪的问题,这也是很多初学者不敢使用多线程的原因.但是在实际开发中,多线程是一 ...

  10. unia-app页面生命周期详解

    A页面,点击按钮传递的参数 <view class="" @tap="gotoLunBo">去轮播页</view> methods:{ ...