go 定义接口解决 import cycle not allowed
前言
go项目运行报错: import cycle not allowed
,导入循环(import cycle)
报错原因,在Go语言中,两个或更多的包之间形成了相互依赖的关系,即A包导入了B包,而B包又反过来导入了A包,形成一个循环。
这种循环会导致编译器无法确定每个包的完整依赖图,因为它们互相引用,就像一个无解的链条。
避免导入循环的关键在于理解包的依赖结构。一般来说,应该尽量保持单向依赖,也就是说,一个包不应该直接或间接地导入其自身,也不应形成一个闭合的循环。
在代码中,可以通过接口类型的变量来使用这些包,而不是直接使用具体的包。
这样你的代码不再依赖于具体的包,而是依赖于接口。
举例一
假设有两个包 pkgA
和 pkgB
,它们需要相互调用对方的功能。我们将通过引入接口来避免循环依赖。
代码示例
文件结构
/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()
}
代码解析
定义接口:在
pkgA
中定义一个接口BInterface
,该接口包含一个方法PerformB()
。这个接口的目的是让pkgA
不需要直接依赖pkgB
,而是依赖于接口。实现接口:在
pkgB
中定义一个结构体StructB
,并实现PerformB()
方法。这样,pkgB
可以在需要调用pkgA
的功能时,通过接口传递自身的实例。使用功能:在
pkgB
的CallA()
方法中,调用pkgA
的PerformA()
方法,并传入StructB
的实例。这样,pkgA
就可以通过接口调用pkgB
的功能,而不需要直接依赖于pkgB
。主函数:在
main.go
中,调用pkgB
的CallA()
方法,启动整个流程。
运行结果
当运行 main.go
时,输出将会是:
Performing B
通过引入接口 BInterface
,我们成功地解耦了 pkgA
和 pkgB
,避免了循环依赖的问题。这种设计使得代码更加灵活和可维护。
举例二
模拟一个简单的支付系统,其中有两个包:payment
和 notification
。
场景描述
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)
}
代码解析
- 定义接口:在
payment
包中,定义了一个接口Notifier
,包含一个方法SendNotification(message string)
。这个接口的目的是让payment
包不直接依赖于notification
包,而是依赖于一个抽象的通知接口。 - 支付处理:在
payment
包中,ProcessPayment
函数负责处理支付逻辑,并在支付成功时调用notifier.SendNotification()
方法。这里的notifier
参数是Notifier
接口的实现。 - 实现通知:在
notification
包中,定义了两个结构体EmailNotifier
和SMSNotifier
,它们都实现了Notifier
接口的SendNotification
方法。 - 主函数:在
main.go
中,我们创建了EmailNotifier
和SMSNotifier
的实例,并将它们传递给ProcessPayment
函数。这样,支付处理逻辑可以根据不同的实现发送通知。
运行结果
当运行 main.go
时,输出将会是:
Email Notification: Payment of $100.00 was successful.
SMS Notification: Payment of $50.00 was successful.
通过引入接口 Notifier
,成功地解耦了 payment
和 notification
包。这样,payment
包不需要知道 notification
包的具体实现,只需要依赖于接口。这种设计使得系统更加灵活,便于扩展和维护。
go 定义接口解决 import cycle not allowed的更多相关文章
- go 报错 import cycle not allowed
运行时报错,import cycle not allowed : 查了goole大概知道了原因,还是导包类的问题,我检察了一下我的代码库,发现我昨天划分几个工具文件,里面的两个文件相互引用,就导致报i ...
- import cycle not allowed in test
写个 sdk 的测试时报错 import cycle not allowed in test 后发现因为测试文件内多写了导入同包路径. 同 package 下的 xxx_test.go 内不需要额外 ...
- linux系统:go build报错import cycle not allowed
go build 困扰我多时的 go 编译报错:循环导入,代码肯定是没问题的,网上查说重新安装go 我觉得也不是太好的办法 import cycle not allowed package day01 ...
- go环境import cycle not allowed问题处理
1.前言 今天在搭建Go语言环境,从https://golang.org/dl/上下载一个Go语言环境.环境变量配置刚开始如下配置: GOROOT=/home/go/bin go执行文件目录 ...
- python定义接口继承类invalid syntax解决办法
class s_all(metaclass=abc.ABCMeta): #python2.7用此方法定义接口继承 # __metaclass__ = abc.ABCMeta @abc.abstract ...
- python定义接口继承类
zxq547 python定义接口继承类invalid syntax解决办法 1 2 3 4 5 6 7 class s_all(metaclass=abc.ABCMeta): #python ...
- struts2 在拦截器进行注入(依据Action是否实现自己定义接口)
比如:经常在Action中都须要获取当前登录的User,就须要获取Session.然后从Session获取当前登录的User,由于这些步骤都是反复操作,能够想办法在拦截器中进行实现.能够自己定义一个接 ...
- 用关键字interface定义接口,通过关键字implements来实现接口
[定义]Java中,能够完成特定功能的,由若干属性和方法组织成的,相对独立的属性和方法的集合. [用途]实现类的多继承,以解决Java只能单继承,不支持多继承的问题. [特点] 用关键字interfa ...
- go定义接口以及类怎么使用接口
go定义接口以及类怎么使用接口 多态是指代码可以根据类型的具体实现采取不同行为的能力.如果一个类型实现了某个接口,所有使用这个接口的地方,都可以支持这种类型的值. 接口是用来定义行为的类型.这些被定义 ...
- JAVA定义接口格式:
[public]interface 接口名称 [extends父接口名列表] { //静态常量 [public] [static] [final] 数据类型变量名=常量值; //抽象方法 [publi ...
随机推荐
- 视频监控推流助手/极低延迟/支持N路批量多线程推流/264和265推流/监控转网页
一.前言说明 搞视频监控开发除了基本的拉流以外,还有个需求是推流,需要将拉到的流重新推流到流媒体服务器,让流媒体服务做转发和负载均衡,这样其他地方只需要问流媒体服务器要视频流即可.为什么拉了又重新推呢 ...
- Qt数据库应用14-超级自定义委托
一.前言 在QTableView.QTreeView以及对于衍生的QTableWidget.QTreeWidget类中,需要用到自定义委托的情形很多,比如提供下拉框选择,进度条展示下载进度啥的,默认的 ...
- Qt音视频开发8-ffmpeg保存裸流
一.前言 最开始做的ffmpeg保存视频文件,就是直接保存的裸流数据,裸流数据一般是H264格式的数据,这种数据文件可以用部分播放器播放,由于不是标准的格式,很多播放器其实不支持的,需要安装对应的解码 ...
- nginx配置参数优化
ginx作为高性能web服务器,即使不特意调整配置参数也可以处理大量的并发请求.以下的配置参数是借鉴网上的一些调优参数,仅作为参考,不见得适于你的线上业务. worker进程 worker_proce ...
- 京东从 OpenStack 改用 Kubernetes 的始末
构建集群的历史 物理机器的时代(2004年-2014年) 在2014年之前,我们公司的应用程序都部署在物理机器上.在物理机器时代,为了给即将上线的应用程序分配物理机器,我们平均需要等上一周的时间.由于 ...
- Superset 用户集成完整方案(iframe方式)
本次集成方案经过个人测试,根据前面2个集成方案的资料,撰写,相关说明由于个人知识水平有限不一定理解准确,有错误的地方环境评论区评论: 1.用户集成方式: A系统用户,通过A的某个界面,iframe嵌入 ...
- Spring,Spring Ioc,Bean详解
Spring框架 Spring框架是Java应用最广的框架,其的成功来自于理念,并非是技术,其中几个理念非常重要,例如IoC(控制反转),AOP(面向切面编程) Spring的优势 低耦合/低侵入(解 ...
- 使用GraalVM将SpringBoot工程编译成平台原生的可执行文件
原文链接:https://blog.liuzijian.com/post/209e68d0-a418-1737-503a-d47e6d2d9350.html 1.GraalVM GraalVM (ht ...
- Windows中利用任务计划执行进程守护
在Windows中除了开发专用的进程守护外,还可以利用任务计划做进程守护. 一.bat示例 tasklist | findstr "notepad" if %ERRORLEVEL% ...
- CBR云备份与恢复管控简介
本文分享自天翼云开发者社区<CBR云备份与恢复管控简介>,作者:c****n 1.CBR云备份与恢复介绍 CBR (Cloud Backup & Restore)是一般云厂商提供的 ...