1
jollywang 66 天前 ![]() 研究了这几个库, 对代码的结构组织结构影响还挺大的, 和 java 的 spring 完全不是一个概念
这么说吧, 不如就是一个全局的 Container struct, 里面定义了各种变量, 需要用的地方再引入, 这样对心智负担也较小, 更容易维护 |
![]() |
2
gowk 66 天前
赞同楼上兄弟说的,感觉 Go 根本没必要用这些 DI 库,徒增复杂度
|
![]() |
3
LitterGopher 66 天前 ![]() 我不会主动使用,但是如果有一个对技术和 golang 半懂不懂的领导一定要求你要使用这个东西——即便他连这个 wire 的基础使用连一知半解都算不上——那我也没有办法。
我不赞同,但是如果你坚持那我就没必要坚持找不快。 |
![]() |
4
matrix1010 66 天前 ![]() 建议调查加上: 你们的 Go 项目写单元测试吗?写单元测试却没有使用依赖注入的情况下是如何 mock 各种依赖的
|
5
DonkeyKane 66 天前 ![]() 小项目不用,大项目不得不用。
|
6
Ayanokouji 66 天前
用依赖注入,但不使用框架。
从这个项目学的思路 https://github.com/mikestefanello/pagoda |
![]() |
7
StarUDream 66 天前
#4 同意,我自己用依赖注入的时候也发现单元测试没法搞。
|
![]() |
8
matrix1010 66 天前
框架以前我都推荐 Grafana ,但 Grafana 太复杂了而且有大量历史遗留代码,很多地方风格也不太统一。有兴趣的可以参考一下我最近的这个项目: https://github.com/Yiling-J/tablepilot. 在 Go 的基础上支持 CLI+WebUI+App, 同时写单元测试
|
![]() |
9
BeautifulSoap 66 天前 via Android
对于说 go 用不到 di 的,我很好奇你们平时用的项目都小到各种依赖能手动注入的吗?
对于大点的项目,注入依赖项肯定需要 di 框架。我主要用 dig |
![]() |
10
BeautifulSoap 66 天前 via Android
@jollywang 全局的 Container struct (说白了就是全局变量存在那的一个 singleton 模式)的话,我就比较好奇,你这存在那的对象是在 init 阶段就 new 好还是等需要的时候再 new (即 lazy init )?后者的话那就必须要考虑线程安全问题。再一个既然是 singleton 了,如果我想要每次调用都生成一个全新的对象的话,是不是还得实现非 singleton 的功能。
那么这一套实现下来,在我看来不就是实现了半个 di 框架了吗 |
11
NotLongNil 66 天前
不用。90% 的项目其实都没必要依赖注入,即使是 java 也一样
|
![]() |
12
MemoryCorner 66 天前
同好奇,如果不用依赖注入,你们单侧依赖怎么 mock ?
|
![]() |
13
seth19960929 66 天前
单元测试和依赖注入没什么关系吧,怎么楼上扯歪了,golang 这么多内置库没用依赖注入也不影响单元测试,一个包内的全局变量,然后包内测试刘 OK 克
|
14
dobelee 66 天前
@MemoryCorner interface+monkey
|
![]() |
15
AEnjoyable 66 天前 via Android
我主导的一个大项目用到了自己写的 ioc ,通过依赖注入来适应不同的 tenants 。
单元测试覆盖所有 ioc 托管对象的接口,业务代码写完只需要去测试环境做一下集成测试就行。 虽然复杂度确实提升了,但后期维护+新增租户啥的都很方便,没啥负担 |
16
CLMan 66 天前
https://www.v2ex.com/t/1094915 半年经贴,回答者都是些熟面孔,回答也类似。
|
17
lanlanye 66 天前
你都用 Go 了……那你猜 Google 为什么要写 wire ?
|
![]() |
18
fgwmlhdkkkw 66 天前
|
19
tairan2006 66 天前
不会,主要是不好用,徒增复杂度。
|
![]() |
20
dwu8555 66 天前
那你为啥不用 java
|
21
fffq 66 天前
不用,那可读性真差
|
22
SimbaPeng 66 天前
wire 、fx 都用过
|
![]() |
23
CoderGeek 66 天前 ![]() 我用的 uber 的 fx 感觉不多不少 凑合用
|
![]() |
24
Jaeger 66 天前
有用 wire ,感觉还是很好用的,使用依赖注入模式会让代码清晰很多,使用 wire 自动生成注入代码可以节省很多工作量。
|
![]() |
25
gl3081 66 天前
微服务使用 DI 框架还是非常方便的,可以灵活组合不同的服务:
参考: https://github.com/moke-game/platform(10+个游戏微服务) |
27
sampeng 66 天前
用过一段时间。心智负担比较大。。。时间长了就给忘了。反正都是 ai 写。就其实还好,多一个 request 问罢了,适当的冗余是很方便查问题的。用的东西多了有时候不是什么好事
|
![]() |
28
NX2023 66 天前
@matrix1010 #4 我之前会弄一个初始化函数替换所有全局变量例如 *gorm.DB ,其他的就直接 gomonkey 了😅
|
29
jollywang 66 天前
@BeautifulSoap 确实, 单测会有些蹩脚, 使用了条件编译来实现
但这个也是受限与小组内开发水平也是参次不齐的限制, java/go/python 都有涉及, 人均负责几个项目, 偶尔需要顶一下. 加之微服务隔离环境, 单个项目粒度控制的比较小, 倒也没那么重吧 |
![]() |
30
NX2023 66 天前 ![]() 我是会用的,当然用依赖注入不一定等于使用依赖注入框架
如果框架我用 https://github.com/samber/do 个人心理洁癖,感觉不依赖注入就跟私拉电线一样这里调一下那里调一下,看着乱心里不舒服( 依赖注入之后等于是把电线捆好放在这里,一个模块一个模块分割的很清晰 当然依赖注入的优点我也是很赞成的,包括容易写测试 |
![]() |
31
haython 66 天前
有些人根本搞不明白依赖注入,所以就一刀切,干脆不学也不用
|
32
changz 66 天前 via Android
wire 加的复杂度在哪里?自己手写和生成区别不大。。。
|
![]() |
33
koujianshusheng 66 天前
上个项目 kratos 里就集成了 wire
可以规范代码 刚开始上手有点难理解觉得 后面用起来就 ok 了 |
34
latifrons 66 天前
我在大量项目里都用了 https://github.com/golobby/container ,支持注解式注入和懒加载,很好用。
其实很多人开发 Go 并没有想清楚以下问题,直接短平快,但是大型项目里往往发现代码越写越乱: 1 ,程序初始化一次性任务、定时任务、长运行服务之间如何安排启动顺序; 2 ,命令行解析和配置文件层级及优先级关系 3 ,RPC/GRPC 服务业务报错/系统报错,错误代码处理 4 ,服务 SIGTERM 优雅退出处理 5 ,日志等级管理( DB ,rpc ,grpc ,resty ) 6 ,单例管理(如*gorm.DB ) 我也是迭代了很多项目,慢慢把这么多东西都抽离成了一个自用独立框架的 |
35
qq135449773 66 天前
当然会用。
对于 DI 这种需求可以说 wire 这种纯静态的方案做的恰到好处。 跑到 runtime 里到处 reflect 那样就有点得不偿失了。 本质上都是到处飞 `NewXXXXX` ,为何不用一种方法让这个过程更高效可读一点? |
36
sotvokun 66 天前
一开始没准备用,但是写到各个组件之间的依赖太多了,手动初始化再在构造函数或者初始化结构体的时候传入也太麻烦了,干脆就直接用 wire 自动生成了。
虽然差别不大,但是生成出来的代码也不用在意人能不能读懂,能用就行,手写的话就要加入可读性维护的考虑了 |
![]() |
37
zengxs 66 天前 ![]() 用 uber/fx 。小项目用不用 DI 无所谓,但是项目比较大的情况下还是有必要用 DI 库的
|
38
HtPM 66 天前
主要还是钱没给够。我未必还要写单元测试?直接手动 request 一把梭哈
|
![]() |
39
HypoChen 66 天前 ![]() 有一个阶段喜欢用 DI 框架,大部分是突增烦恼。
事实上,只需要使用「将 Interface 定义在用的地方,而不是实现的地方」这个小技巧,就基本可以获得 DI 的好处了 |
40
devhxy 66 天前
推荐一个轻量级的:github.com/samber/do
|
41
Visionhope 66 天前 ![]() 之前用的 wire ,新项目试了下 fx ,感觉要流畅一些。至于使用 DI 框架的好处,那就是定义好初始化函数,剩下的交给框架就行了。个人还是蛮喜欢的。
|
![]() |
42
testliyu 66 天前
我们项目里没做
|
![]() |
43
qloog 66 天前
用到了,使用 google 的 wire: https://github.com/go-eagle/eagle-layout
|
![]() |
44
totoro52 65 天前
依赖注入啥时候和 java 绑定并成了一种邪恶的东西呢。。。他明明是一种思想
|
![]() |
45
zthxxx 65 天前 ![]() 目前 uber/fx
|
46
james122333 65 天前 via Android
这之前已经有讨论过了 我倾向不用
package 下直接初始化 struct 就可以了 一楼讲的其实就是把 struct 当命名空间用 很 js 的方式 go 已经有命名空间了 所以不用这种方式实现一样结果更单纯 spring 下的与一楼讲的相似 只不过是换成阵列 徒增性能损耗 这种方式因语言本身 import 功能就已经解决依赖问题 只要没 import 循环问题即可 其它语言的都可能会有依赖循环问题 这是种对语言特性深度了解并使用本身具备的功能减少过多嵌套的方式个人认为最佳 至于测试... 可以用 go test 本身就已经有的功能 |
47
james122333 65 天前 via Android
虽然我本身不怎么想写测试
|
48
james122333 65 天前 via Android
还有一点 直接用 import 在编译或开发时就会检查依赖了 错了代表设计不合理
|
![]() |
49
wangritian 65 天前
学习了本贴和一些链接的内容,然后决定继续手搓
|
![]() |
50
Trim21 65 天前 ![]() 就算用 di 也不该用 dig ,应该用 fx (
|
![]() |
51
lujiaxing 65 天前
|
![]() |
52
zjsxwc 65 天前
wire 、dig 之流太恶心,不如直接用 init 来依赖注入,当然要注意 init 顺序
|
![]() |
54
sophos 65 天前
哈哈哈,月经贴鉴定完毕,总结一下 :-)
- 个人的、不写单测的项目不需要用依赖注入,代码抽象的复杂度会上升 - 多人参与的、迭代频繁的项目,用依赖注入会让大家都舒服,用类似的模式写代码和测试 刚好回头刷了下我早前开源的 Go 依赖注入框架,发现多了几个 star…… https://github.com/go-kod |
55
1283095131 65 天前 ![]() fx 挺好用的
|
![]() |
56
mengyx 65 天前 ![]() 以前用过 wire ,现在用 fx 比较多;不用依赖注入的话,业务的单元测试会很难写
|
![]() |
57
lysShub 65 天前
@matrix1010 1.设计的时候解耦一下、测试专门搞个 mock 实现; 2. go 有几个 monkey 库可以用
|
![]() |
58
kkhaike 65 天前
说 fx 好用的我都点了赞
|
![]() |
59
NoobPhper 65 天前
把一件复杂的事情做简单 是件很难得事情看来
|
![]() |
60
EscYezi 65 天前 via iPhone
以前试过依赖注入框架,后来看 flutter 的时候发现了 riverpod 用的 provider 方式,自己写的小项目也改成类似的方式感觉比较顺手,还可以借助一点 go 本身的循环依赖检查
https://gist.github.com/yeziyezi/97d4e75dc59e5d9d40f3d88889552cb5 |
61
mayli 65 天前
|
62
higker 65 天前
Go 语言最大优势就是携程并发和大包 native code 这些... PL 设计层面 错误处理 defer 关键资源关闭 和 锁颗粒度冲突 隐式接口 大小写反射问题 最小封装单位是包 而且 struct 是不能有统一的构造函数,只能在包上做构造函数,玩设计模式就残废。
依赖注入相比动态 VM 语言简直就是残废,通过代码生成来玩,或者一些开发者就不关注这个问题,直接引编码进去,好的项目结构应该是每个组件使用统一构造函数进行依赖注入。没有依赖注入,写单元测试却没有使用依赖注入的情况下是如何 mock 各种依赖的。很多程序员根本搞不明白依赖注入,(其实就是狗屎代码,或者第一门 PL 就是 Go 这种垃圾语言),所以就一刀切干脆不学也不用。 Go 这种语言依赖注入目前解决方案就是通过静态分析,要么自己把项目结构设计好一点,手动构造函数初始化,不写测试。拿去写一些中间件可以,写业务还不如回去写 js 语言。不要被一些国内一些 sx 布道师吹着多么多么好,误入到这个坑里面,目前云原生基础设施可以去学 Rust ,Java 云原生方案也要很多 Spring Native Qurakus Vert.x graalvm.org AOT 这些... |
63
james122333 65 天前 via Android
@higker
go 没有建构子但你可以自己定义函数在注入时候 new 和运行 并返回自己 可搭配界面和范型 依赖注入个人觉得是非必要功能 因为它做的其实很有限 但非要的话显式注入可追溯更好 可以看看我讲的 含测试 多数依赖注入其实做的事情就是静态变量在做的事情 多绕了一圈代码不是很美的 在包内的静态变量还更好管理 因为可以用包来分类 依赖注入的初始化只是种不同写法 就我用来写业务感受其实没什么分别 活用语言特性否则你用哪种语言都一样 只能一直当框架调包侠 其实框架做的事情也就那样 |
![]() |
65
server 64 天前
没有 wire, 真解耦不了
|
66
momo2789 64 天前
不用 wire 代码能看么
|
67
fds 64 天前
@mayli https://uber-go.github.io/fx/index.html AI 说是 uber 在 dig 上面又包装了一层,更友好。我看接口确实不错。
|
68
zaihuilvcha 64 天前
go 邪教徒习惯宣称“如果我要用这个那我为啥不去写 java”,恰如 rust 邪教徒习惯宣称“xxx 为啥不用 rust 实现”。都是意识形态大于一切
|
![]() |
69
lesismal 64 天前
@fds #67 看了下 fx 教程的例子,像是得了什么大病才能把本来简单的事情搞得这么复杂。。。
@zaihuilvcha #68 抛开事实效果就说是邪教——这本身也是一种邪教思维,建议就事论事。 @momo2789 #66 我看过的绝大多数优秀项目源码都没用 wire ,确切地说,我个人视角比较局限、看过的所有优秀源码或者源码模块都没用 wire 。 @latifrons #34 没有 di 也没遇到过这些烦恼,难道没有 di 就没法实现这些了?增加了 di 反倒把这些实现看上去更复杂了,多数是设计模式的重度用户觉得没有 di 不好罢了 @matrix1010 #4 比如 net 包用于测试的 type pipe struct ,很多实现 interface 即可。我到现在都没搞懂依赖注入到底是啥,看一两分钟就觉得是在把简单问题复杂化所以实在花不了时间深入研究它到底是啥,关键是,现实中没有遇到不用它就搞不了的场景。 |
![]() |
70
matrix1010 64 天前
@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 类项目很常见,但功能单一依赖很少的单一功能库可能不太需要
|
![]() |
71
lesismal 64 天前
@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 的人当然不算什么,即使不懂的人简单看看也不算什么负担,但相比于本可以简简单单的方式,毕竟还是多了一道弯弯绕 |
![]() |
72
matrix1010 64 天前
@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 的方法
|
![]() |
73
lesismal 64 天前
@matrix1010 #72
其实是一样的,不论大小、依赖的多少,没有本质区别。 你说它不考虑顺序,但你还是要手动去注册这些,用于注册的这些 func 也都是要按格式实现。 mock 的本质是同一类行为,泛化一点多态的概念、可以认为它是一种多态。 oo 可以多态,c++的模板可以静多态,interface 也可以多态,甚至最简单的 callback func 也可以认为是多态,甚至脚本语言里、连返回值之类的格式不一样之类的但只要你可以用统一的方式调用(例如忽略返回值)也可以是多态,再甚至,只要支持闭包、都用闭包也可以简简单单实现多态。 本质上讲,di ,mock 是这种多态,callback (例如 middleware )也是这种多态。不用 dig/fx/wire 这些,真没感觉任何障碍,包括你贴的 granfana 这段。 |
![]() |
74
matrix1010 64 天前
@lesismal 在依赖复杂的情况下顺序是个大问题。因为依赖本身是个 graph ,你加入一个新的 service ,这个 service 会依赖一些已有的 services ,这里问题不大。但假如一部分已有的 services 也依赖这个新的 service ,那所有 services 的初始化顺序可能就要改变。不使用 DI 框架的情况下你需要人工(AI 也许也行)来判断这个新 service 初始化的代码应该放哪里,以及是否要调整已有 services 的初始化顺序。修改某个 service 的依赖时也是一样的情况
|
75
lixikei 63 天前
|
![]() |
76
hzzhzzdogee 63 天前
@zaihuilvcha true
|
![]() |
77
lesismal 63 天前
@matrix1010 #74
> 不使用 DI 框架的情况下你需要人工(AI 也许也行)来判断这个新 service 初始化的代码应该放哪里,以及是否要调整已有 services 的初始化顺序 那么 di 怎样保证这个顺序? |
![]() |
78
lesismal 63 天前
@matrix1010
grafana 这种业务本身采集依赖多的,用 di 确实是比初始化的各种都放在一大坨的地方看上去整洁了些,这个我支持。 但是其他更多实际项目中,又有多少个项目像 grafana 这种怪物有这么多种依赖,绝大多数项目都不是真正需要,比如你举例自己的这个项目,没那么多依赖,加了这层 di 没有更简洁、看着不直观、像我这种不熟悉 dig 的人会觉得更复杂了。 |
![]() |
79
matrix1010 63 天前 ![]() @lesismal Topological sort of the directed graph defined by the providers using a depth-first search using a stack. https://github.com/google/wire/blob/main/internal/wire/analyze.go#L105 。web 类项目从开始就用 DI 框架其实是我的个人习惯,因为这类项目总会趋向于越来越复杂。我的 cache 库就没有 DI 框架,因为功能很单一项目复杂度不会一直增长
|
80
fds 63 天前 ![]() @lesismal 是,多了层抽象嘛,如果业务本身不复杂确实没什么必要。
我觉得比较实用的地方是修改已有代码。比如给一个使用比较多的服务增加一个依赖。不用 DI 就得在所有用到这个服务的地方增加新参数,有 DI 就只用改下声明。对于很多人合作的项目,每个人就可以只改自己负责的代码,不用碰其他人负责的文件。当然这不能避免出错,只是心理负担小一些。 |
81
wwhontheway 63 天前
至少我接触过的项目都不用,徒增复杂度
|