当前位置:首页 > 科技  > 软件

Go 包循环引用及对策,你学会了吗?

来源: 责编: 时间:2024-03-18 09:37:21 103观看
导读引言从 Java 转到 Go 的开发同学,大概都会踩到第一个“坑”:Go 的包循环引用。Go 的包循环引用是什么意思呢?有一定经验的开发者都知道循环依赖,比如 A 依赖了 B, B 依赖了 C ,C 又依赖了 A。这就构成了一个循环依赖(有环图)

引言

从 Java 转到 Go 的开发同学,大概都会踩到第一个“坑”:Go 的包循环引用。FYn28资讯网——每日最新资讯28at.com

Go 的包循环引用是什么意思呢?有一定经验的开发者都知道循环依赖,比如 A 依赖了 B, B 依赖了 C ,C 又依赖了 A。这就构成了一个循环依赖(有环图)。FYn28资讯网——每日最新资讯28at.com

在 Java 里面,循环依赖是类级别的;但 Go 里要更严格一些:Go 的循环引用判定是 包级别的。举个例子,包 A 下的类 A 依赖了包 B 下的类 B,类 B 又依赖了包 C 下的类 C, 类 C 又依赖了包 A 下的 D。在 Java 里面,这里并没有构成循环依赖。但在 Go 里面,这导致了包循环引用:包 A => 包 B = > 包 C => 包 A。Go 会编译不通过:报 import circle not allowed。FYn28资讯网——每日最新资讯28at.com

对包依赖不太重视的人,初期会感到不适应。本文举几个自己踩过的坑,作一说明。FYn28资讯网——每日最新资讯28at.com

包循环引用释例

对象导致的包循环引用

哎呀呀,初来乍到,一下子给我来了八个包循环引用,打击得我有点不知所措了。这是怎么回事呢 ?FYn28资讯网——每日最新资讯28at.com

图片图片FYn28资讯网——每日最新资讯28at.com

第一个循环引用,是因为上报的包 agent 下对象 DetectionBase 依赖了包 denoise 下的降噪模型结果对象 HitDenoiseModel,而在某一处,HitDenoiseModel  又引用了包 agent 下的另一个对象。FYn28资讯网——每日最新资讯28at.com

为什么会发生这个事情?这是因为,图省事,我直接把输出对象放到了输入对象的包里,不想复制一份。正确的做法是,输入的对象只能放输入对象,不能放输出对象,否则依赖就会扩大出去。FYn28资讯网——每日最新资讯28at.com

如何解决?有两种方案:FYn28资讯网——每日最新资讯28at.com

  • 把 HitDenoiseModel 放在 agent 包下。然后定义另一个对象 denoise/DenoiseResultModel,将 FillHitDenoiseModel 方法移到包 denoise 下,HitDenoiseModel 赋值给 DenoiseResultModel。这样形成了包 denoise => agent 的单向依赖,保证“输出 =>输入” 的单向依赖。不过,这种方法还是存在“篡改”原始数据的小罪行。
  • 不对 DetectionBase 做任何变更,新创建一个包和对象,把 HitDenoiseModel 放在新创建对象里,同时将 FillHitDenoiseModel 方法移到新创建对象的包下。3

启示:避免篡改输入对象。应用中的对象往往是很多的,不太重视包依赖,很容易造成对象的循环引用。即使你自己能够小心确保不出问题,也会和别人添加的类造成循环引用。FYn28资讯网——每日最新资讯28at.com

发送消息与接收消息循环引用

梅开二度。又来了一个包循环引用。FYn28资讯网——每日最新资讯28at.com

图片图片FYn28资讯网——每日最新资讯28at.com

这又是怎么回事?有了第一次经验,第二次就不那么慌了。先提个 MR ,看看引入了哪些类。尤其是循环引用的那条链路。我们看到 cdc/msg 这个地方作为起点开始循环。为什么会有循环呢?因为我本来打算把 msg 相关的消息对象和消息处理都放在一起。但这种方式很容易导致 循环引用。为什么呢?因为 引入消息对象的时候, 就会把包下的所有类引入的所有包都引进来,这样就会把 /cdc/msg/XXXReceiver 引入,进而引入 /cdc/handler/XXXhandler, 而 msg/handler/XXXhandler 又会引入 msg/XXXSender。就导致了包循环引用依赖。FYn28资讯网——每日最新资讯28at.com

看来之前还是随意惯了。FYn28资讯网——每日最新资讯28at.com

图片图片FYn28资讯网——每日最新资讯28at.com

怎么解决?把 XXXReceiver 放在包 receiver 下,把 XXXSender 放在包 sender 下即可。FYn28资讯网——每日最新资讯28at.com

启示:FYn28资讯网——每日最新资讯28at.com

  • 引入类的包要小,这样就很类似 Java 的全限定性包,减少循环引用的可能性。
  • 简单的消息对象与复杂的消息处理不要放在一起,因为引入简单的就会把复杂的引入,复杂的又会递归引入更多的依赖。

internal 引用了 share

一键三连。FYn28资讯网——每日最新资讯28at.com

下图又是怎么回事?借鉴业界最佳实践,咱们把一个工程下的代码分为了 internal 和 share。其中 internal 的代码不能被其它模块访问,只有 share 下的代码作为桥梁,为其它模块提供服务。FYn28资讯网——每日最新资讯28at.com

图片图片FYn28资讯网——每日最新资讯28at.com

这个是因为 internal/detect_config/AService 引用了 share/detect_config/BService ,然后 share/detect_config/CService 又引用了 internal/detect_config/DService。internal 包怎么能够引用 share 下的包呢 ?内部模块怎么能够依赖外部模块 ?世界似乎不那么美好了。FYn28资讯网——每日最新资讯28at.com

与同事讨论,他们认为,helper 不应该依赖 service ,而应该依赖 repository, 而我一直认为 helper 是对 service 提供服务的一种高层封装, helper 依赖 service 是很正常。helper 依赖 repository, 看上去说得也很有道理。不过 internal 模块依赖 share 模块,看上去总是感觉有点违反单向依赖的设计原则。FYn28资讯网——每日最新资讯28at.com

经过讨论后,我和同事各做了修改。我的修改是让 helper 依赖 repository, 同事的修改是,把原来 service 拆分成公共的 service 和内部的 service,保证 share 对 internal 的单向依赖。FYn28资讯网——每日最新资讯28at.com

运行时循环依赖

即使你幸运地逃过了包循环引用的检测,但存在运行时循环依赖,Go 会直接卡住。FYn28资讯网——每日最新资讯28at.com

一个例子如下图所示:有若干个流程组件 A, B, C,加载到一个工厂 F 下,由一个 执行器 E 依次从 F 中取出执行。但是呢,在流程组件C 中,又依赖了 E 来执行任务。这样会导致循环依赖。在 Java 中,Spring 会忽略组件 C, 初始化成功,但运行时找不到 C 而导致流程出错(可以用懒加载机制解决);但是 Go 就不那么幸运了(也许是幸运的,因为它让你早点发现问题)。FYn28资讯网——每日最新资讯28at.com

对于这种场景,可以用消息队列来解耦。FYn28资讯网——每日最新资讯28at.com

图片图片FYn28资讯网——每日最新资讯28at.com

对策汇总

遇到包循环引用,有哪些经验可循呢 ?FYn28资讯网——每日最新资讯28at.com

(1) 提倡小而独立的包。不要把大量的有关联的类都放在一个包下。这样,很容易因为一个类的引入,而引入更多依赖,导致依赖不可控。FYn28资讯网——每日最新资讯28at.com

(2)单向依赖原则。internal 下的类不应当依赖 share 下的类。因为 share 下的类一定会依赖 internal。如果 internal 又依赖 share ,就破坏了“单向依赖”原则。当工程越来越大时,一定会有一个点会爆发。不是不报,时候未到。细分包可以缓解这种问题,但不能从根本上避免单向依赖的破坏。FYn28资讯网——每日最新资讯28at.com

(3) 依赖倒置原则。尽量依赖接口,而不是具体实现类。FYn28资讯网——每日最新资讯28at.com

(4)使用消息队列解耦依赖。FYn28资讯网——每日最新资讯28at.com

(5)相对合理的依赖方向:model => (constants, 无依赖的 model) ; dto => types => constants ;util => (dto, types, constants, models);service =>  repository => model ;helper => (repository, util, cache) ; controller => (helper, service) ;  receiver => handler => (service, helper)FYn28资讯网——每日最新资讯28at.com

最后问一句:Go 为什么不允许包循环引用呢 ?听听 Go 语言作者怎么说:FYn28资讯网——每日最新资讯28at.com

图片图片FYn28资讯网——每日最新资讯28at.com

再听听 AI 怎么说 :FYn28资讯网——每日最新资讯28at.com

图片FYn28资讯网——每日最新资讯28at.com

小结

本文讨论了几个包循环引用的例子,并给出了相应的对策。FYn28资讯网——每日最新资讯28at.com

个人觉得,这种禁止循环引用的做法还是可取的,能培养良好的设计习惯。软件开发,本质是应对结构复杂性的技艺。而设计思维,则是应对结构复杂性的重要法宝。开发人员应多多学习设计思考,保持系统和架构的优雅。FYn28资讯网——每日最新资讯28at.com

参考资料

  • go循环依赖最佳解决方案[1]

Reference

[1]go循环依赖最佳解决方案:https://juejin.cn/post/7290389972406501432FYn28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-76488-0.htmlGo 包循环引用及对策,你学会了吗?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: JQuery 4.0震撼发布:这是复兴还是告别?

下一篇: 业务开发做到零 bug 有多难?

标签:
  • 热门焦点
  • Redmi Pad评测:红米充满野心的一次尝试

    Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • JavaScript 混淆及反混淆代码工具

    JavaScript 混淆及反混淆代码工具

    介绍在我们开始学习反混淆之前,我们首先要了解一下代码混淆。如果不了解代码是如何混淆的,我们可能无法成功对代码进行反混淆,尤其是使用自定义混淆器对其进行混淆时。什么是混
  • 自动化在DevOps中的力量:简化软件开发和交付

    自动化在DevOps中的力量:简化软件开发和交付

    自动化在DevOps中扮演着重要角色,它提升了DevOps的效能。通过自动化工具和方法,DevOps团队可以实现以下目标:消除手动和重复性任务。简化流程。在整个软件开发生命周期中实现更
  • 使用LLM插件从命令行访问Llama 2

    使用LLM插件从命令行访问Llama 2

    最近的一个大新闻是Meta AI推出了新的开源授权的大型语言模型Llama 2。这是一项非常重要的进展:Llama 2可免费用于研究和商业用途。(几小时前,swyy发现它已从LLaMA 2更名为Lla
  • 品牌洞察丨服务本地,美团直播成效几何?

    品牌洞察丨服务本地,美团直播成效几何?

    来源:17PR7月11日,美团App首页推荐位出现“美团直播”的固定入口。在直播聚合页面,外卖“神枪手”直播间、美团旅行直播间、美团买菜直播间等均已上线,同时
  • 当家的盒马,加速谋生

    当家的盒马,加速谋生

    来源 | 价值星球Planet作者 | 归去来自己“当家”的盒马,开始加速谋生了。据盒马官微消息,盒马计划今年开放生鲜供应链,将其生鲜商品送往食堂。目前,盒马在上海已经与
  • 自研Exynos回归!三星Galaxy S24系列将提供Exynos和骁龙双版本

    自研Exynos回归!三星Galaxy S24系列将提供Exynos和骁龙双版本

    年初,全新的三星Galaxy S23系列发布,包含Galaxy S23、Galaxy S23+和Galaxy S23 Ultra三个版本,全系搭载超频版骁龙8 Gen 2,虽同样采用台积电4nm工艺制
  • iQOO Neo8 Pro即将开售:到手价3099元起 安卓性能最强旗舰

    iQOO Neo8 Pro即将开售:到手价3099元起 安卓性能最强旗舰

    5月23日,iQOO如期举行了新品发布会,全新的iQOO Neo8系列也正式与大家见面,包含iQOO Neo8和iQOO Neo8 Pro两个版本,其中标准版搭载高通骁龙8+,而Pro版更
  • 微软发布Windows 11新版 引入全新任务栏状态

    微软发布Windows 11新版 引入全新任务栏状态

    近日,微软发布了Windows 11新版,而Build 22563更新主要引入了几周前曝光的平板模式任务栏等,系统更流畅了。更新中,Windows 11加入了专门针对平板优化的任务栏
Top