本人之前写 Java ,目前是 Go 语言新手。
前不久阿里开源了一款 Go 的 IoC 框架,由此引发我的好奇。搜索了一下发现 Go 还存在挺多 IoC 框架的。 只是作为新手有点好奇,面向过程语言真的需要 IoC 吗?
我理解是 Java 有 Spring 是因为它是纯面向对象的,没有函数这个概念只有方法,所以在一些需要函数的地方就需要通过 IoC 来注入一个单例。我作为使用者感觉到的是,IoC 是面向对象语言对于一些无状态的逻辑的妥协。
但是像 Go 这种面向过程的,不是就直接调用那个函数就好了吗?真的需要将 Go 写成 Java 的样子吗?
还请各位指点一下新人,提前感谢!
1
e7 2022-06-28 10:37:59 +08:00
解耦嘛,大家都喜欢,跟语言无关
|
2
yekern 2022-06-28 10:42:18 +08:00
Go 小白一个勿喷.
只能说说自己的理解,我最近也在看, IoC 的本质是啥,就是反射, 反射用在最多的地方就是 框架(脚手架)的底层,可以更好的 协作框架 依赖注入各种组件,增加业务开发时候的舒适性和效率, 如果追求极致的性能和更高的并发 不建议使用 IoC 如果 是后台管理之类的,为了更快的开发速度和更好的开发体验则可以使用. 不过话说回来 Go 的反射效率真心不高, 而且项目 都是中间件,微服务,可以子项目都很小几乎都用不到,这玩意就开发大型单体项目能用. |
3
ql562482472 2022-06-28 10:44:17 +08:00 1
如果你没有全局变量 要 ioc 有什么意义 完全没意义嘛
|
4
zhuangzhuang1988 2022-06-28 10:46:03 +08:00 2
需要的 项目复杂了没 ioc 真不好搞
哪怕前端复杂了都需要 ioc vscode 是自己有个简单的 ioc https://github.dev/microsoft/vscode/blob/0656d21d11910e1b241d7d6c52761d89c0ba23a4/src/vs/platform/instantiation/common/serviceCollection.ts 最近阿里搞了个 opensumi 虽然是魔改 vscode, 也搞了个 ioc https://opensumi.com/zh/docs/develop/basic-design/dependence-injector elipse theia 就直接用公开方案 InversifyJS 了 https://theia-ide.org/docs/services_and_contributions/ |
5
Sunxy88 OP |
6
keepeye 2022-06-28 10:49:10 +08:00
依赖注入嘛,一种解耦的手段,哪种语言都有啊,实现的方式差异罢了
|
7
zhuangzhuang1988 2022-06-28 11:09:36 +08:00
@Sunxy88 面向过程要做得好就每个函数都传入一个 context
比如 https://github.com/mruby/mruby/blob/master/include/mruby.h#L255 或者 lua 的第一个参数也是 context |
8
hxtheone 2022-06-28 11:14:21 +08:00 via iPhone
感觉中小规模的 Go 项目手动管理依赖并不是多难以接受的问题, 本身 Go 项目大量的功能都是通过函数来暴露的, 需要实例化依赖的场景比 Java 少, 而且 Go 结构体的动态实例化能力也不如 Java 的类, 导致 Ioc 不是基于反射就是用代码生成器, 说实话都不对我的味口
|
9
zoowii 2022-06-28 11:16:07 +08:00
IOC 是必须的,到中等规模项目,没用 IOC 框架你也会自己被动造出一个原始版本的 IOC
|
10
xuzhzzz 2022-06-28 11:19:03 +08:00 1
学一下 wire ,这个是用的最多的。想了解一下反射的实现可以看下 uber 的 dig/fx 。阿里开源的这个先观望吧。。
|
11
statumer 2022-06-28 11:20:21 +08:00 via iPhone
你这种理解是错的,再好好想想面向过程是什么含义。。
函数本身是无状态的,要让它有意义必须要有上下文,上下文通过函数参数注入,管理函数上下文最简单的方法是一个上下文对象的指针(比如 this 指针)。不管你用什么语言都要管理各种对象的生命周期,都要有某种程度的依赖注入,只是 go 没有 Java 那种注解+扫描所以没那么自动化 |
12
28Sv0ngQfIE7Yloe 2022-06-28 11:21:46 +08:00
go 没有注解,搞 ioc 感觉心智负担比 context 传来传去还重(个人感觉,没有说 go 不好的意思)
|
13
Sh4p 2022-06-28 11:26:22 +08:00
意义在于你会不会做一层隐藏具体实现的封装。
比如你某些存储用的 KV ,今天用 Redis ,明天用 TiKV ,后天用自研了。但只要都是 KV 的,那你用它的时候就十有八九还是 get(k): V 这种姿势。 那么你可以封装出一个 persistence interface ,以后底层换实现了,只要操心写配置文件+注入,然后改改这个 persistence interface 就好了,其它模块都不用动的。 |
14
TWorldIsNButThis 2022-06-28 11:28:45 +08:00 via iPhone 2
Spring 主要是要增强 bean 的功能
不然直接 static import 也没什么问题 |
15
xhinliang 2022-06-28 11:33:02 +08:00
意义在于你会不会做一层隐藏具体实现的封装。
比如你某些存储用的 KV ,今天用 Redis ,明天用 TiKV ,后天用自研了。但只要都是 KV 的,那你用它的时候就十有八九还是 get(k): V 这种姿势。 那么你可以封装出一个 persistence interface ,以后底层换实现了,只要操心写配置文件+注入,然后改改这个 persistence interface 就好了,其它模块都不用动的。 -------- 我用 IoC 这么久,很少遇到你说的这种换底层实现的情况。 个人觉得 IoC + 面向接口设计,最大的好处在于写单测比较方便。 |
16
timethinker 2022-06-28 11:57:55 +08:00
Ioc 的主要作用就是读取组件声明( XML/注解 /代码注册),也就是构造 IoC ,然后实例化和管理这些组件的生命周期。
根据构造组件所需要的定义(面向对象语言中通常是 class 的构造函数),就可以得到创建这些组件所需要的先后顺序,从而不必手动去硬编码这个过程。 另外 IoC 本身的生命周期(创建 /销毁)形成了一个 scope 作用域,从而可以管理存在于容器内的各种实例。 所以一般 IoC 有两个阶段,一个阶段是读取声明(注册组件,构造 IoC ),另一个阶段是构造和管理组件。 对于非面向对象的语言,我能想到的就是在读取组件定义这一块(即对应面向对象中 class 的构造函数),转变为了一个 Key 和包含回调函数的 struct 。 这个 key 可能就是一个简单的字符串,类似 map 或者 dict 中的 Key 。 这个 struct 由 IoC 定义,里面包含了初始化定义(依赖描述),生命周期回调函数(函数签名由 IoC 定义),以及一个指向实际值的 interface{}指针。 上面说到 IoC 本身也是有生命周期的,它提供了一种作用域的语义,因此可以很方便的管理一系列组件的实例。 另外说一下 SpringBoot 的各种 starter 也只是提供了一个默认的组装布局,或者称之为开箱即用的默认配置,只不过可以根据配置文件来自动进行组装逻辑,这就需要提供一种通用的描述规范。 |
17
wangyzj 2022-06-28 12:13:20 +08:00 3
不希望某天 golang 变成另外一个 java
|
18
Saxton 2022-06-28 12:48:22 +08:00 5
你们是不是理解错什么了? IOC 是一种思想啊,怎么和语言挂上钩了啊
麻烦各位思想打开一点好不好,比如楼上这位。 |
19
wangyzj 2022-06-28 13:28:19 +08:00
@Saxton #18 麻烦你读个题好么?
”想讨论一下阿里开源的 Go 的 IoC 框架以及 Go 是不是真的需要 ioc“ |
20
swulling 2022-06-28 13:33:42 +08:00 via iPhone
我倾向于不用 ioc ,如果你的 go 应用复杂到得用 ioc ,是否考虑拆分为多个微服务?
|
21
swulling 2022-06-28 13:34:40 +08:00 via iPhone
阿里这个框架我看了,因为 go 的语言限制大量采用了代码生成。而我对代码生成是持怀疑态度的,这里面真有问题 debug 会非常复杂。
|
22
THESDZ 2022-06-28 13:41:30 +08:00
我觉得多用 接口+实现类的方式即可,类似于 database/sql 可以在实现里面去注册到 database/sql 中,通过引入不同的实现类即可切换。
至于楼上说反射的,我觉得不见得对,起码要我来实现 ioc 的话,也是通过定义 interface+init()方法初始化来做。 |
23
Abirdcfly 2022-06-28 13:44:08 +08:00
golang 里哪个大一些,知名的项目在用 IOC 么?我目前还没用到,想学习一下。😂
|
24
frozenshadow 2022-06-28 13:46:27 +08:00 via Android
@xhinliang 如果是为了测试和解耦的话,我理解 golang 的 interface 类型是不是就可以满足。
|
25
cheng6563 2022-06-28 13:49:29 +08:00
go 语言本身做不到 IoC 自动装配,要硬上 IoC 就得用代码生成器,但代码生成器会直接破坏原生简洁的构建方式。
IoC 一般需要语言支持一些元编程才比较好用,go 就是个基本毫无元编程特性的语言,强上 IoC 我认为是徒增烦恼。 |
26
Saxton 2022-06-28 13:57:27 +08:00
@wangyzj 你说的这句话就已经脱离这个话题了,ioc 不是 java 的属性,他也不是任何一门语言的属性,他是一种思想,就跟 MVC 一样,难不成你在 go 里用 mvc 思想也是要把 go 变成跟 java 一样吗。
|
27
joesonw 2022-06-28 14:21:01 +08:00 via iPhone
反射的有 fx ,代码生成的有 wire 。
|
29
GeruzoniAnsasu 2022-06-28 15:24:50 +08:00 20
有啥好看的,拿锤看钉罢了
第一性原理: IOC 是什么,解决了什么问题 - 是什么: 当我在一个纯 OO 语言里,想用到某个 object 提供的方法时(比如 A 类的实例)需要 a=new A; a.method() 但我不希望当前写的这个逻辑依赖死 A 类,我希望有个「只要跟 A 类差不多的类实例」就行。于是可以使用接口+工厂,即 a=AFactory.New();a.method(),但这个实现依然要依赖特定的工厂,我希望写任何逻辑都能有一个万能工厂提供我要的接口,于是我不再在逻辑里写 a=new A 了,我写 a=THEGOD.provide("A"),或者在我的构造函数里写 CreateMe(I_WANT_AN_A a),这样我就有 a 了,怎么来的我不管,谁爱给谁给。 - 解决什么问题: 1. A 经常变化,我无法自己得到正确的 A ,但 GOD 可以,因为 A 会告诉 GOD 怎么生成自己 2. A 就算变了,我不需要配合改变,「代价」比较小 好,现在思考这两个问题在 golang 语境的意义 先说代价。因为这是最显著的差异 golang 是一个需要编译成二进制的静态类型语言,而且所有的代码都在同一个二进制里 它不像 java ,改了某一个模块,仅仅是那个模块的字节码发生变化。java 甚至可以动态加载字节码,也就说它完全可以 core 保持运行,临时下线一些功能,加载新的字节码,将原功能指到新加载的字节码上。 但 golang 不行。 ****任意代码的修改都会导致整个程序重新编译**** 编译时长之类的问题可以通过缓存文件优化,但是 ****服务程序必须全部停止**** 这一点是不会改变的。无论怎么折腾,golang 写的代码都必须重新编译,且**完全停止**原来的二进制程序运行再跑一个新的。 因此相比于 java 语境中「代价」有重大的运行时意义,golang 语境下「代价较小」的意义仅仅是保持代码工程的相对整洁罢了,但源码组织是整个软件工程中相当不重要的一环,我想没什么人会否认吧。大家都住在屎山,但没听说哪里的代码写成喜马拉雅能跑写成屎山就跑不了的。 再说问题 1 // service.go var a IA = NewA() a.method() // interface.go type IA interface{ method() } func NewA() IA { if 1{return a.NewA()} if 2 {return a.v2.NewA()} } 不行吗? service 不引用 A 吧 interface 要改,但跟改 xml 本质上没什么区别吧 另外静态语言都不热衷这种框架,因为就算语言提供反射,它们也不像动态语言一样能「凭空造出一个类和实例」,从本质来说它们只能「改变指向类型的指针」,因此只要类型结构本身没有编译到二进制里,无论怎么折腾也造不出一个新类型。 这也就意味着 IOC 框架再怎么折腾也需要一个已经存在的类型实现,而非只要有存根就行。 因此在这些语言里,让框架为使用者寻找一个 IOC 容器的意义也仅仅只是在一个编译期就确定的类型映射表里找一个合适的类型罢了 那我代码里直接用这个静态映射不就好了,我干嘛还要「委托框架来帮我找」 ---- 拿你提的这个框架来说 1. 不使用这个框架,只用 interface 是不是也能从代码上解耦? 2. 使用这个框架,接口实现变化是不是一样的要「改代码+重新编译+重新部署」而且改动范围差不多? golang 生态在微服务化跟 java 生态在 bean 化都是需求和语言特性 /限制的「磨合」,反向照猫画虎反正我的心态都是看个笑话 |
30
blless 2022-06-28 15:30:56 +08:00
IOC 是面向业务的,小项目估计无所谓,大项目业务复杂程度没有一些基础框架支持都自己实现挺麻烦的。不过开源的 wire 跟 dig 已经很好用了
|
31
EscYezi 2022-06-28 15:32:06 +08:00 via iPhone
比较好奇除了 ioc 还有什么其他好的依赖管理方案
|
32
fpure 2022-06-28 15:34:29 +08:00
现在一般用 spring ioc 的目的是为了 aop ,没有 aop 的 ioc 没有多大价值(解耦除外)
|
33
Yoock 2022-06-28 15:41:52 +08:00
不需要 IOC
|
34
EminemW 2022-06-28 16:14:24 +08:00 via iPhone
你们不用 IOC 的话,那些有状态的 service 都用全局变量存着么,比如 mysql, redis 之类的,虽然可以自己手动传进去,我用 ioc 就是省了到处 new ,不过我不会用阿里开源的东西,怕了
|
35
XCFOX 2022-06-28 16:48:09 +08:00
借楼提问:
按理来说后端开发里的 Service 类、Controller 类都是只被实例化一次,个人觉得这个复杂度根本不需要一个容器来管理依赖。个人觉得对象真正多的场景是游戏开发里,一个场景里经常包含成百上千的对象。 那么游戏开发领域会经常使用 IOC/DI 工具吗? |
36
RubyJack 2022-06-28 16:55:18 +08:00 1
不需要的,只有搞 java 业务开发的人喜欢 ioc ,稍微底层一些的应用都不喜欢这玩意,可以看看 cpp 、rust 、golang 的大型开源项目,几乎见不到 ioc
|
37
GeruzoniAnsasu 2022-06-28 17:49:05 +08:00 1
@XCFOX
我写过一丁点 unity ,unity 本身就非常 DI 。对于场景设计器之类的东西静态定义的对象,框架控制着对象容器,写的逻辑会被框架反向绑定到对象上,逻辑类( XXBehavior : Behavior )是通过 unity 提供的 API 来获取自己绑定的物体实例的,物体不持有逻辑逻辑也不持有物体 |
38
zmal 2022-06-28 17:56:18 +08:00
“我理解是 Java 有 Spring 是因为它是纯面向对象的,没有函数这个概念只有方法,所以在一些需要函数的地方就需要通过 IoC 来注入一个单例。我作为使用者感觉到的是,IoC 是面向对象语言对于一些无状态的逻辑的妥协。”
个人这个认知不太准确,Java 的 IOC 框架 1 是为了解耦,2 是为了切面。单例对象是附赠功能。 不存在哪个语言应该是什么样子,语言最终会趋向于用户认为更好用的模样。 |
39
joesonw 2022-06-28 18:22:04 +08:00 via iPhone
全局变量的依赖和初始化顺序只能在外面注入的地方统一处理好,ioc 是每个服务的初始化和依赖都是自己声明自己的,我提供这个服务的依赖改变了其他人没必要知道。
|
42
charlie21 2022-06-28 19:51:41 +08:00
关于依赖注入的一篇文章 为什么有很多人说 Go 语言不需要依赖注入? golang
https://www.zhihu.com/question/265433666/answer/337599960 依赖注入,可说是任何程序员必掌握的技巧之一,系统设计入门基础技能之一。而依赖倒置原则作为一条原则,自从被人发现以来,从来都没被颠覆过,可以算得上软件设计艺术的真理之一。 |
43
securityCoding 2022-06-28 20:22:54 +08:00
多用用 interface ,mock 起来不要太爽,至于 ioc 可有可无
|
44
iosyyy 2022-06-28 20:26:50 +08:00
@cheng6563 简洁是指 err 漫天飞吗..
@GeruzoniAnsasu "静态语言都不热衷这种框架" 你知道 ioc 起源自哪里吗? 起源自 cpp 最经典的静态语言 另外你举这个例子本身就不符合开闭原则程序更改只能对内进行更改 但是引入 xml 文件程序更改就是对外的了 因为配置文件可以理解为程序外部的一部分 你觉得是更改一个 xml 中一个字段容易呢 还是几千行代码里面改一段不知道哪个函数里面的代码容易呢?(这里我还是想说尤其是在 go 语言 err 满天飞的情况下). 另外关于屎山的观点 诚然程序写多了会导致系统有很多"奇奇怪怪"的代码 但我们要做的是避免这些的出现 不能因为屎山能跑就一味的拥抱她 虽然程序总会化为屎山但是我们写的代码是可能变成屎山里面的黄金的代码与方法从来都是我们在用而不是乃作屎山 |
47
GeruzoniAnsasu 2022-06-28 22:36:55 +08:00
@iosyyy CRTP 是一个经典又独特的 idiom, which 完美体现了「反转」是怎么一回事,但 cpp 哪个项目试图把 spring 那一套在 cpp 上重新造一次吗? 就算我孤陋寡闻没听说过「可能被拿出来举例的项目」,但那个项目——它不流行吧?也不在 cpp 的主流视野里吧?
你看了知乎,也看了阿里这个框架想干什么的话就会明白我说的「拿锤看钉」是什么意思,就跟在 cpp 里写 template<class T, class F > class IoCContainer{std::variant<T> obj} class MyService : public IocContainer<MyService, Feature<Service, AutoWire>> {} 一样,也不说徒劳吧,反正看着滑稽是肯定的,因为 cpp 里模板参数本身就可以当成接口来用,根本没必要去模拟注解的功能,也模拟不来,顶多仿照一个静态版本——而静态版本↑模板参数本身就可以当成接口用,service 的使用者只要写 template<class S> class UsingService{} 然后在 UsingService 实例化的地方(而非 UsingService 内,UsingService 并不直接依赖 Service ,而是自己的模板参数)给 UsingService 派发 MyService 就好,这本身就是 IoC 作为设计原则的体现,而非造一个看起来像 spring 的框架,那根本就不合适啊。 另外开闭原则指的是(广义版)逻辑模块的开闭,你这直接把整个程序都看成不可分割的一个大逻辑块的看法整得我有点无语 |
48
24bit 2022-06-28 23:00:19 +08:00
在我看来 Java 中的 DI 是转移复杂度的一种方式,将 Bean 的装配转移到框架完成,开发者只需要标记需要什么 Bean 就行了。
Java 纯 OOP 的设计会导致项目中存在大量的类和接口,同时会创建出大量的 Bean ,手动装配这些 Bean 是一件很费事的事情,因此使用 DI 是很有效的转移复杂度,减负增效的方式。 而 Golang 虽然支持一定的 OOP 特性,但中小型项目中类似 Java 中需要装配大量 Bean 的场景并不是很多,引入基于代码生成或反射的 DI 框架会反而会带来额外的成本,增加复杂度。 Golang 本身是一个「简陋」的语言,硬是往 Java 的模式上靠往往会带来额外的复杂度。 |
49
CoderGeek 2022-06-28 23:44:31 +08:00 1
|
50
iseki 2022-06-29 02:17:58 +08:00
还没有去看这个东西,只说下个人感觉。IoC 需要但 IoC 框架之于 Go 可能有点重了。
IoC 是个思想自不必说。但 IoC 框架··· 这种框架用来在大量使用此类范式时降低人的工作量,但是 Go 是一个动态性不足的语言,和 Java 那种动不动运行时动态字节码生成不同,基本没法这么干;其次静态连接的 Go 也很难像经典 Java 一样根据配置随时加载不同的依赖项,像 Java 那样大规模 IoC 并不会有特别多的好处,是否需要为了解决一点问题而引入一整个框架存疑。 |
51
tairan2006 2022-06-29 08:45:44 +08:00
DI 还是需要的,抽象接口多重实现。不过我一般都是手动注入的,小项目没必要搞个框架。
大项目可能需要一套吧,或者考虑微服务拆分? |
52
iosyyy 2022-06-29 12:27:36 +08:00
@GeruzoniAnsasu 不流行? 不流行从来就不代表不行 cpp 主要是写内核系统这种要求速度快的程序的 像 linux 代码中一大堆 goto 你能说 goto 就是好东西?? 另外开闭原则指的是"软件中的对象(类,模块,函数等等)"对修改关闭对扩展开启, 从来就不是你说的对于逻辑模块 这属于偷换概念 而且代码什么时候不是逻辑模块了呢? 另外 ioc 从来都不只是 spring 你上面提出的观点也是对于 ioc 本身而不是 spring 和楼主提出的阿里框架
|
53
GeruzoniAnsasu 2022-06-29 14:58:59 +08:00 1
@iosyyy 你擅长记概念这点我看出来了
那么我替你总结一下你表达过的观点 1. cpp 也有 ioc 框架 2. 改代码不符合开闭原则但改 xml 符合 3. 不要屎山 4. 不流行的做法不代表 bad practice 噢…… 除了第二点不同意其它的我同意啊 所以为啥要有个像 spring 的框架呢,它会好用吗? OP: > 真的需要将 Go 写成 Java 的样子吗 #29 : 我的观点是作为一种指导思想,在不同语言里有适合各自的表达形式,复制一个相似的框架并不能起到足够引人注目的效果,而且同一种设计在不同语言机制下能带来的意义也不同。因此我认为不需要或者不应该把 go 写成 java 的样子。 上述观点我后来又重复地显式地总结了一次 #47 : > … 本身就是 IoC 作为设计原则的体现,而非造一个看起来像 spring 的框架,那根本就不合适啊 这一点我们共识吗? |
54
izzy27 2022-06-30 00:01:17 +08:00
写得 go 不多,但是目前的认知觉得 go 并不需要 spring 这种 IOC 框架
|
55
ecnelises 2022-07-02 01:57:21 +08:00 via iPhone
想起一个笑话:
为什么 C#里没有 Spring ?因为微软没有春天。 |