V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  a132811  ›  全部回复第 7 页 / 共 20 页
回复总数  384
1 ... 3  4  5  6  7  8  9  10  11  12 ... 20  
2022-08-06 17:27:25 +08:00
回复了 a132811 创建的主题 Go 编程语言 感觉 uber/fx 并不比 getInstance 工厂好用
@kkhaike 感谢你的回复。

你给的 fx 用例—— 说明了 fx 能提供很方便的自动依赖注入、lifecycle 、按需创建等等(用 fx.Module 还可以进一步实现对复杂依赖的模块化管理)

但是回避了我说的问题(这取决于你是否这认为这些是问题,如果你认为这些问题可以忽略,那么 fx 自然是很好用; 而我认为这些问题较为重要,大多数情况可通过构造工厂避免,并且不降低项目的可维护性)

我描述的场景,都属于白盒,需要从内部进行,而不是在外围顶层运行:
`fxtest.New(t, app.Provides(), fx.Populate(&m)).RequireStart().RequireStop()`


1. 手动维护依赖属性的场景
>> 可以看出。实际上加属性,也只需要修改一个地方,即 dep 成员,整个也很简单"
你描述的用法属于单个 fx.New 的场景,只需要维护一份依赖关系就行了。

但是我的场景是:**由于要测试、临时运行局部函数**, 此时就需要创建很多不同的 fx.New

**为什么不能避免重新创建 fx.New 呢?**
因为要运行的是局部代码,我想把测试本身也放在局部,局部代码的依赖关系也不一样。所以就不能复用入口层 main.go 中的 fx.New()

fx.In/Out/module 确实能方便的组织依赖关系,但是手动管理 provides 是不能省掉的,用工厂就省掉这一步的。


2. **不完美**的按需创建
fx 虽然提供了注入时才自动创建,但是不能做到 **更完美**的按需创建。

比如,我们使用 fx 为 Service 注入依赖 DB, 创建 Service 时 fx 会自动创建并注入 DB 对象,即使不会真正使用到 DB 对象.

为什么明明依赖了 db 对象,却又不使用它呢?

因为我只是想调试一下单个的方法 Service.FetchApi() ,它并不需要 db 对象,却必须初始化不相关各种 db 对象(有的 db 连接还限制频率、连接失败的问题)

当然了,可以 mock db 嘛,但是有几个问题:只能在注入前 mock 而不能注入后再 mock; 有些 library 并没有暴露私有属性的 mock 方法; 有些复杂的库自身也没有 mock 的能力,比如 uber cadence 并没有 mock-cadene-client 这种东西。

真实的情况,需要 mock 的东西可能会很复杂,如果去研究怎么 mock 就很浪费时间。

3. 单元测试有点麻烦
你提到的
>> Provide 间 UT ,由于 fx 的特殊原因,以及 go 的循环引用问题,所以 Provide 间 UT 只能用在
黑盒测试,我是这么处理的
>> 有一个 app 模块 管理所有 Provide/Invoke ,main 直接调用 app.App().Start() 启动程序
>> ```
>> func TestXXX(t *testing.T) {
>> var m some.M
>> defer fxtest.New(t, app.Provides(), fx.Populate(&m)).RequireStart().RequireStop()
>> }

你这份代码,是在外围顶层 main.go 中建了一份`fxtest.New` 然后 start 运行测试代码。
缺点是:1. 无法测试的局部代码的私有方法 2.需要手动维护测试所需的`app.Provides()`, 3. 外部的 invoke 与内部的被测代码是分离的,所以必须先把被测代码 import 出来。4. 我更喜欢`model/{user.go, user_test.go}` 测试与代码放到一起,并且不需要在外围引入`fxtest.New().Start()`.

我得重复一下问题——fx 确实能避免手动 New ,但是不能避免**手动管理**依赖关系.

我在本文描述中提到的单测 case 就是这样的,简单的说就是:

第一,要手写不同测试所需的不同的 provide ,第二, 要定义一些私有属性来存放注入的对象(复杂测试也需要有专属的依赖注入)

依赖越多,手动写的工作量就就越多.

在 go 下,这种情况把 OOP 换成函数式,此类手动管理的代码,可不需要写,会更简单

4. Lifecycle
一般情况我对 lifecycle 没有特别高要求,需要关停的就那么几个,平滑退出可直接在入口层统一处理

//main.go
func main(){
.....
defer close()
<-ctx.Done()
}
func close(){
if PgFactory.Exists(): PgFactory.Close()
....
EsFactory.Close()
KafkaFactory.Close()
closeHttpServer()
....
}

也可以自定义 context, 把所有要释放的方法注入到全局的 ctx, 然后用 cancel 或者 ctx.Cancel()触发释放


// main.go
serverCtx, cancel := WithCancelFns(context.Background())
....
defer cancel()

// service.go
getServerCtx().AddCancelFns(
func() { println("close pg") },
func() { println("close server") },
)

5. go generate 生成的
>> app 模块甚至都是 go generate 生成的
并不清楚你的 app 是什么实现。
我的经验是 fxTest 初始化的样板代码,我可以用 ide/vscode 的 snippet 一键一生成,只是不同的依赖管理+属性的初始化,不能抽象为模板,还是要手写

6. 循环引用
之所提这个问题,只是为了回应前一位 v 友——循环问题只跟代码结构设计有关,跟 fx 本身无关
> ps, v 站不支持回复加 markdown 缩进全被吃了:)

7. 性能损失
赞同你说的, 确实不关紧要

8. fx 调试
正常调用是没有问题,除了特例:

比如依赖 /接口是自动注入的,当我想了解这个接口到底是哪里初始化的,是需要先了解整个项目的依赖关系图的。而单例工厂则是显式的、直观的。(当处理一个不熟悉的复杂项目时,就会有这个感受)
2022-08-04 12:50:15 +08:00
回复了 a132811 创建的主题 Go 编程语言 感觉 uber/fx 并不比 getInstance 工厂好用
@YuuuuuuH 这里其实就是 setUp 函数,初始化了 fx.New/Module/Invoke 。

不过,由于需要的 provide 依赖不同,所以需要写各种 setUp 函数。
@cmdOptionKana
我认为没有工会并不是 996 的根源,那不过是一种形式(其实有些公司是有工会的),主要看你怎么定义工会了。真正伤害了普遍员工利益的话,大家也是可以组织起来的。我记得 15 年某些地方出租车 ba gong 的事。

我不认为 996 的根源我们这两年批判的资本家,欧美日就是反例,之前就有人投过票,字节非常多的人反对取消大小周。

大家想想自己为什么 996 ,为什么不敢离职、裸辞,来一次说走就走的旅行。路是自己走的,工作是自己选的,为什么不敢?到底是什么大山把自己压着?

为什么 996 已经明确是严重违法的的,却几乎没有人站出来?

为什么现代生产效率提高了,活得却更累了、自由时间越来越少了?

为什么教育医疗资源不足同时,却有越来越多的医学生、师范生内卷,甚至毕业就失业?

为什么房价依然很高的同时,房地产行业却不景气?

是不是太魔幻了?
用来看 deno 的源码
2022-08-01 19:42:16 +08:00
回复了 a132811 创建的主题 Vue.js Vue3 下有什么 umijs 的替代品推荐吗?
@liximomo
---------忘记说背景了, 放假了就没关注-------

也并不是 umijs 有多难用。主要我们的 umijs 旧项目,构建越很慢,走 k8s 部署更慢。

很旧没有升级了,过去积累的 warning 非常多了(不全是 umijs 的,还有其它各种包),然后升级 msfu 遇到些困难(k8s 那里部署就有问题,用了 docker 多步构建)。

以前也升级过,升级体验并不太好。

然后看了下 vue3 的 vite+unocss+...,有一点心动。
2022-07-29 20:01:54 +08:00
回复了 a132811 创建的主题 Go 编程语言 感觉 uber/fx 并不比 getInstance 工厂好用
@blless 不是很同意。

如果没有做好代码分层,无论是 fx ,还是 getInstance ,都无法避免循环依赖。

可能和你想的 getInstance 不一样,我说是构造工厂

1. 我所需要的对象都来自于工厂,调用者不必关心工厂怎么初始化的,也不关心工厂如何实现的、更不关心工厂依赖什么
2. 调用者**只关心工作的输入输出**符合接口规范。这跟 fx 是一致的。
3. 如果工厂如果出错了,应该保证返回 err 或者像 fx 那样返回”failed to build xxx“的 panic
4. 不必要的依赖跟 mock 是两个层面的事情吧。如果是不必要的依赖,最好直接不初始化才对

不太理解你说的`getInstance 再 getInstance 循环`是什么情况,循环依赖在编译前就会报错不是吗?

说太多不如上一段实际的代码,拿我们的之前一个项目来说,简化结构如下:

```
app/
settings/
fx.go
config.go
store/
fx.go
cache/
fx.go
redlock/
fx.go
redlock.go
redlock_test.go
pg/
es/
kafka/
....
service/
service1/
service2/
....
main.go
```

虽然引入了 fx, 但是加功能测试`redlock_test`时就发现有循环依赖:
```
$ cat ./store/cache/redlock/redlock_test.go
func TestRedlock(t *testing.T) {
fx.New(
settings.Module,
fx.Provide(xxx),
....
}
$ go test -v ./store/cache/redlock/
package app/store/cached/redlock
imports app/store
imports app/service
imports app/settings: import cycle not allowed in test
FAIL app/store/cache/redlock [setup failed]
```

原因是, 因为 app/settings/fx.go 包含了依赖了各种配置的构造器

```
import (
"app/service"
"app/store/pg"
"app/store/es"
"app/service/..."
...
)


// yaml 文件配置解析
func GetConfig() (config *Config) {
...
}

// 各种配置的构造器
func NewRedisUniversalOptions(config *Config) *redis.UniversalOptions {... }
func NewPostgresOptions(config *Config) *pg.Options {...}
func NewEsOptions(config *Config) *es.Options {...}
func NewService1Options(config *Config) *srv1.Options {...}
....

var Module = fx.Module("settings",
fx.Provide(
GetConfig,
NewRedisUniversalOptions,
NewPgOptions,
NewEsOptions,
NewService1Config,
....
),
)
```

可以看到
1. setting 基础配置,依赖其它种 store(rddis/es/pg/kafka...),甚至 service 的 Options 类
2. setting 是其它库的上层,而不是底层。只有顶层的 main.go 可调用 setting ,其它不行
3. 我们就不能在其它包内引用这个 setting, 想引用的话就需要重新对配置解耦合。

还是那句话,无论是工厂还是 fx ,代码如果没有做好良好的解耦分层,项目都不会那么好维护。
2022-07-27 15:29:39 +08:00
回复了 Ufo666 创建的主题 Java 以后安装一个软件是不是可以直接用 docker 啊
@MengiNo 用-v 映射,就无法自动补全了。除非`-v /data/demo:/data/demo` 保持一致
vlang 还是在认真做事的。而木兰的作者已经为虚假宣传道歉了,跟透明计算,还有红芯浏览器没有什么区别
2022-07-26 22:53:35 +08:00
回复了 u2gign 创建的主题 分享发现 没人讨论稚晖君的键盘吗,好想有一个
稚晖君很强大,不过这款键盘还没有多少让我更换键盘 atom84 的冲动,除了那个空格键的滑动不错。
谁能设计一款让我丢弃 macbook touchpad 的键盘,我保证冲动。( tinkpad 的小红点可不好用)
2022-07-23 15:29:27 +08:00
回复了 a132811 创建的主题 Go 编程语言 感觉 uber/fx 并不比 getInstance 工厂好用
@a132811 说“唯一的区别”肯定不对。我使用感受是:
1. fx 提供了生命周期的管理,保证模块加载、退出的有序性。则单纯靠单例工厂,就不容易做。
2. fx 查看整个项目的执行的依赖比较直观。

其它区别(优点),我想不到什么了
2022-07-23 15:11:55 +08:00
回复了 a132811 创建的主题 Go 编程语言 感觉 uber/fx 并不比 getInstance 工厂好用
@mcfog 是否可展开解释一下?

1. 我例子中的测试代码 TestWorkflow 不用 fx 有更好的方式吗?这个情况我倒是很常用
2. 用单例工厂 不等于 堆代码堆逻辑 吧?单例工厂把每个实例缓存放在私有变量。即然使用到 fx 管理代码,本身就使用了单例模式(每个依赖就是一个单例)。如果是单例变变例,也是一样的管理。唯一的区别是——单例的缓存实例是放在 package 作用域,fx 是把缓存实例放在 app 内
3. IDE 只能自动处理一小部分 snippet ,就是我上面说的样板代码(单测 1 ),我写测试时还是要手动维护额外的依赖管理(单测 2 )
2022-07-21 14:23:49 +08:00
回复了 a132811 创建的主题 Go 编程语言 感觉 uber/fx 并不比 getInstance 工厂好用
@janxin
有 mock, 但不全 mock ,不追求所有的东西都 mock ,算是介于单测和功能测试之间吧。

另外,性能损失以及调试问题不大,所以我把这两个痛点放在了最后。


@pmx1990 我的使用感受跟你不太一样,我是觉得大项目使用 getInstance 工厂来组织代码会比 Fx 好用。
不知道
为了避免 getInstance 混乱,需要坚持一些原则:存放实例缓存的命名统一前缀如`_inner_`、缓存变量小写私有、
工厂名要有统一前缀、实例工厂放在统一固定文件名、严格遵守 package 的上下分层。也能做到 fx.Module 那样清晰组织代码的
感觉这东西完全可以做成 PWA 应用
2022-07-21 00:09:57 +08:00
回复了 bam9 创建的主题 程序员 nodejs 中文网 开始收费了,已经付费了的同学举个爪
我在想,站长是不是最近买了房,要还房贷啊?

我支持收费,据说是 MIT 协议的,好像也说得过去——就是不知道有没有问其它 PR 贡献者意见

无论翻译的好还是差,贵还是便宜,明码标价,合情合理——虽然有违传播知识的初忠

不过我感觉,从程序员身上挣这个钱,有点难——虽然中文用户的基数大
直白一点,护肤记 /护肤瓶
2022-07-18 17:16:41 +08:00
回复了 iLionel 创建的主题 分享创造 有没有经常弄混 macOS 输入法的?我实在是受不了了 😤
@iLionel vim 按 esc 会进入命令模式,此时如果是中文输入法必须要手动切换成英文
--------
我发现 neovim v0.7.2 居然可以自动切换中英文
2022-07-18 16:29:32 +08:00
回复了 iLionel 创建的主题 分享创造 有没有经常弄混 macOS 输入法的?我实在是受不了了 😤
我用的清歌五笔,倒是内置了 Switch keyboards based on apps

但是不能实现 vim 按 esc 自动切英文,你这个能支持吗?
2022-07-08 12:33:28 +08:00
回复了 moremoney 创建的主题 程序员 JetBrains 涨价了,你的工资涨了吗
@acapla 什么公司这么良心?
1 ... 3  4  5  6  7  8  9  10  11  12 ... 20  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2015 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 32ms · UTC 16:16 · PVG 00:16 · LAX 09:16 · JFK 12:16
Developed with CodeLauncher
♥ Do have faith in what you're doing.