做个调查:你们的 Go 项目中会用到依赖注入吗?比如用到如 wire、dig 之类的库?

3 天前
 inSpring
6079 次点击
所在节点    Go 编程语言
76 条回复
mayli
1 天前
higker
1 天前
Go 语言最大优势就是携程并发和大包 native code 这些... PL 设计层面 错误处理 defer 关键资源关闭 和 锁颗粒度冲突 隐式接口 大小写反射问题 最小封装单位是包 而且 struct 是不能有统一的构造函数,只能在包上做构造函数,玩设计模式就残废。

依赖注入相比动态 VM 语言简直就是残废,通过代码生成来玩,或者一些开发者就不关注这个问题,直接引编码进去,好的项目结构应该是每个组件使用统一构造函数进行依赖注入。没有依赖注入,写单元测试却没有使用依赖注入的情况下是如何 mock 各种依赖的。很多程序员根本搞不明白依赖注入,(其实就是狗屎代码,或者第一门 PL 就是 Go 这种垃圾语言),所以就一刀切干脆不学也不用。

Go 这种语言依赖注入目前解决方案就是通过静态分析,要么自己把项目结构设计好一点,手动构造函数初始化,不写测试。拿去写一些中间件可以,写业务还不如回去写 js 语言。不要被一些国内一些 sx 布道师吹着多么多么好,误入到这个坑里面,目前云原生基础设施可以去学 Rust ,Java 云原生方案也要很多 Spring Native Qurakus Vert.x graalvm.org AOT 这些...
james122333
1 天前
@higker

go 没有建构子但你可以自己定义函数在注入时候 new 和运行 并返回自己 可搭配界面和范型

依赖注入个人觉得是非必要功能 因为它做的其实很有限 但非要的话显式注入可追溯更好 可以看看我讲的 含测试

多数依赖注入其实做的事情就是静态变量在做的事情
多绕了一圈代码不是很美的 在包内的静态变量还更好管理 因为可以用包来分类 依赖注入的初始化只是种不同写法

就我用来写业务感受其实没什么分别 活用语言特性否则你用哪种语言都一样 只能一直当框架调包侠 其实框架做的事情也就那样
Trim21
1 天前
@mayli dig 的 readme 里面提到的那个 fx
server
1 天前
没有 wire, 真解耦不了
momo2789
1 天前
不用 wire 代码能看么
fds
1 天前
@mayli https://uber-go.github.io/fx/index.html AI 说是 uber 在 dig 上面又包装了一层,更友好。我看接口确实不错。
zaihuilvcha
1 天前
go 邪教徒习惯宣称“如果我要用这个那我为啥不去写 java”,恰如 rust 邪教徒习惯宣称“xxx 为啥不用 rust 实现”。都是意识形态大于一切
lesismal
22 小时 21 分钟前
@fds #67 看了下 fx 教程的例子,像是得了什么大病才能把本来简单的事情搞得这么复杂。。。

@zaihuilvcha #68 抛开事实效果就说是邪教——这本身也是一种邪教思维,建议就事论事。

@momo2789 #66 我看过的绝大多数优秀项目源码都没用 wire ,确切地说,我个人视角比较局限、看过的所有优秀源码或者源码模块都没用 wire 。

@latifrons #34 没有 di 也没遇到过这些烦恼,难道没有 di 就没法实现这些了?增加了 di 反倒把这些实现看上去更复杂了,多数是设计模式的重度用户觉得没有 di 不好罢了

@matrix1010 #4 比如 net 包用于测试的 type pipe struct ,很多实现 interface 即可。我到现在都没搞懂依赖注入到底是啥,看一两分钟就觉得是在把简单问题复杂化所以实在花不了时间深入研究它到底是啥,关键是,现实中没有遇到不用它就搞不了的场景。
matrix1010
22 小时 12 分钟前
@lesismal 我自己这个项目就很直观 https://github.com/Yiling-J/tablepilot/blob/main/services/table/table.go ,我要给 table service 写测试,table service 依赖 ai service ,ai service 有自己的测试不用再测一遍所以直接 mock 。为了能 mock 就需要把 ai service 在创建 table service 时传进去,这就是依赖注入。di 在 web 类项目很常见,但功能单一依赖很少的单一功能库可能不太需要
lesismal
21 小时 35 分钟前
@matrix1010 #70 但这里的 di 并不是必需品吧

相比于:
err = container.Provide(ai.NewAiService, dig.As(new((ai.AiService))))
if err != nil {
panic(err)
}
err = container.Provide(table.NewTableService, dig.As(new((table.TableService))))
if err != nil {
panic(err)
}

简简单单的这种也可以:
aiService, err := ai.NewAiService(...)
if err != nil {
panic(err)
}
tableService, err := table.NewTableService(config, db, aiService...)
if err != nil {
panic(err)
}


多个 Provide 的都是这种,而且每个 Provide 也都有 if err ,真没看出来哪里变得简化了,反倒增加了 di 本身的隐含逻辑需要额外去理解、更绕了
这里最核心的能够方便 mock 的,就是 dig.As ,而这个本质上是实现 interface 。
至于 di 本身的逻辑、对于熟悉设计模式熟悉 di 的人当然不算什么,即使不懂的人简单看看也不算什么负担,但相比于本可以简简单单的方式,毕竟还是多了一道弯弯绕
matrix1010
21 小时 9 分钟前
@lesismal 我的比较简单,但 grafana 就不一样了。这是 server: https://github.com/grafana/grafana/blob/main/pkg/api/http_server.go#L340 ,这是 DI: https://github.com/grafana/grafana/blob/main/pkg/server/wire.go#L204 。DI 框架的好处是不用考虑顺序,框架会自动处理,同时初始化时也不用手写各种 NewXxx 的方法
lesismal
20 小时 54 分钟前
@matrix1010 #72
其实是一样的,不论大小、依赖的多少,没有本质区别。
你说它不考虑顺序,但你还是要手动去注册这些,用于注册的这些 func 也都是要按格式实现。

mock 的本质是同一类行为,泛化一点多态的概念、可以认为它是一种多态。
oo 可以多态,c++的模板可以静多态,interface 也可以多态,甚至最简单的 callback func 也可以认为是多态,甚至脚本语言里、连返回值之类的格式不一样之类的但只要你可以用统一的方式调用(例如忽略返回值)也可以是多态,再甚至,只要支持闭包、都用闭包也可以简简单单实现多态。

本质上讲,di ,mock 是这种多态,callback (例如 middleware )也是这种多态。不用 dig/fx/wire 这些,真没感觉任何障碍,包括你贴的 granfana 这段。
matrix1010
20 小时 24 分钟前
@lesismal 在依赖复杂的情况下顺序是个大问题。因为依赖本身是个 graph ,你加入一个新的 service ,这个 service 会依赖一些已有的 services ,这里问题不大。但假如一部分已有的 services 也依赖这个新的 service ,那所有 services 的初始化顺序可能就要改变。不使用 DI 框架的情况下你需要人工(AI 也许也行)来判断这个新 service 初始化的代码应该放哪里,以及是否要调整已有 services 的初始化顺序。修改某个 service 的依赖时也是一样的情况
lixikei
16 小时 45 分钟前
hzzhzzdogee
3 小时 1 分钟前

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/1131027

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX