3

不限语言,谈谈如何避免循环依赖?

 2 years ago
source link: https://www.v2ex.com/t/877841
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  Java

不限语言,谈谈如何避免循环依赖?

  vczyh · 4 小时 8 分钟前 · 1350 次点击

如何避免循环依赖,这个其实和语言没什么关系。

解决方式也比较简单,但是随着业务复杂起来,比较容易出现这种情况。

就用 Java 来说,大家怎么避免 service 互相调用的。

44 条回复    2022-09-05 18:47:23 +08:00
Kontinue

Kontinue      4 小时 4 分钟前

1. 设计问题,重新设计
2. 学阿里,加一层 manager 层
sunjiayao

sunjiayao      4 小时 4 分钟前

接口和实现分不同的 module 。实现 module 禁止其他模块引用。多个接口模块需要同时引用的类放到 common-module ,common 不引用任何模块
VeryZero

VeryZero      3 小时 59 分钟前

分层,如果发现 service 互相调用,就再加一层。。
vczyh

vczyh      3 小时 58 分钟前

@Kontinue 那 service 调用 service 是否合理呢,还是直接禁止 service 依赖其他 service ,如果需要组合那么在 manager ( facade )层干这件事。
superB

superB      3 小时 58 分钟前

只要一個 God Class 就不依賴的
vczyh

vczyh      3 小时 58 分钟前

@VeryZero 那这样是不是直接禁止 service 互相调用更好?
vczyh

vczyh      3 小时 56 分钟前

@Very
@vczyh 写错了,应该是禁止 service 注入别的 service
Kontinue

Kontinue      3 小时 53 分钟前

@vczyh
我觉得 service 调用 service 是合理的,但是互相调用那就不合理了。。
1. 一般设计时候能想到的公共方法,一般会单独抽出来,放到 manager 层或者跟 2L 说的一样,搞一个 common module 。
2. 但写着写着,可能会出现新的 service 会用到之前 service 的某个方法,然后就开始互相调用了哈哈
Akitora

Akitora      3 小时 48 分钟前

把需要调用的另一个服务的方法在上一层作为 callback 参数传进服务去
frank1256

frank1256      3 小时 48 分钟前

无脑用,写完了,不同语言写工具去检测,spring 的 ioc ,或者 go 的 go-wire ,你让人去设计,不如让工具检测来的方便
Jooooooooo

Jooooooooo      3 小时 46 分钟前

刻意追求这种规定干啥...
optional

optional      3 小时 43 分钟前 via iPhone

application service 禁止互相调用
hay313955795

hay313955795      3 小时 42 分钟前

..循环依赖???不是有个懒加载就能解决了吗.现在没问题 能跑就行..等以后出了问题再说呗
sadfQED2

sadfQED2      3 小时 41 分钟前 via Android

xuanbg

xuanbg      3 小时 30 分钟前

我从来不考虑循环依赖问题,因为我写代码前先画个思维导图。我就问你思维导图怎么产生交叉?
vczyh

vczyh      3 小时 18 分钟前

@frank1256 主要是检查出来后又得重新设计,不如在设计之初想想如何避免。
vczyh

vczyh      3 小时 17 分钟前

@Jooooooooo 不是追求规定,如果一开始没有规定或者设计不合理,之后大概率出现循环依赖。
Jooooooooo

Jooooooooo      3 小时 15 分钟前

@vczyh 有很多场景循环依赖是最简单合理的设计, 我认为刻意是规避没有意义.
vczyh

vczyh      3 小时 14 分钟前

@xuanbg 你其实说到重点了,最根本的就是设计问题,谁依赖谁在设计之初就决定了,如果大家都了解那么没有问题,但是在业务发展的过程中,没有 review 很有可能破坏规则,那么可以可以在工程代码上指定一些规范来尽量避免,比如说:禁止 service 调用 service ?
frank1256

frank1256      3 小时 13 分钟前

@vczyh 不是额,检查出来就提示哪里出现循环了,改掉那一处就行了,设计之初就想好的话,太考验设计了。代码里噼里啪啦的 service 疯狂创建,来不及设计了
vczyh

vczyh      3 小时 13 分钟前

@Jooooooooo spring 是一定程度上是支持的,但是 Golang 是禁止的。
vczyh

vczyh      3 小时 13 分钟前

@VeryZero 那这一层一般叫什么呢?
vczyh

vczyh      3 小时 12 分钟前

@Kontinue 是的,主要是后面会出现的,前期没有这个问题。
vczyh

vczyh      3 小时 12 分钟前

@frank1256 事实确实是你说的那样,很蛋疼。
libook

libook      3 小时 11 分钟前

同级尽量避免互相调用,看是否重新规划服务层级和为服务划分是否能解决问题。
vczyh

vczyh      3 小时 10 分钟前

有没有同学有比较好的实践
vczyh

vczyh      3 小时 9 分钟前

@libook 我也是这么认为
fiypig

fiypig      3 小时 9 分钟前

= = Java 真的会出现这情况,那时候在学就是这样,反倒 go 就不会出现这情况,没那么乱。。。
ChoateYao

ChoateYao      3 小时 9 分钟前

Module 之间加多一个 转换层把其他 Service 的接口当成远程接口,模块内也不存在 Service 互相调用的情况,如果出现那么一定是业务逻辑抽离不干净。
Jooooooooo

Jooooooooo      3 小时 7 分钟前

@vczyh 你说的是系统层面的循环调用, 我说的是业务层面的循环调用.

如果你真想解决问题, 得解决后面这个问题. 前面这个问题更是一个小问题.
vczyh

vczyh      3 小时 4 分钟前

@ChoateYao 那意思是
之前:UserService.get(long id):获取用户信息->根据用户 ID 获取所有订单信息(造成 UserService 依赖 OrderService )
改成:为了禁止 service 之间调用,在 service 层上加一层,在这一层组合 UserService 和 OrderService
可以这么理解吗?
ChoateYao

ChoateYao      2 小时 59 分钟前

@vczyh 你的场景是 OrderService 依赖 UserService ,你的主要场景是订单,不是用户。

加多一层 ACL (防腐层)

UserInfoAdpater 主要实现调用 UserService 的 get 方法放在 OrderModule 的 ACL 里面,转换成你想要的 User 对象

在 OrderService 里面调用 UserInfoAdpater 得到 User 对象之后再查询 User Order Items
nothingistrue

nothingistrue      2 小时 45 分钟前   ❤️ 2

@vczyh #32 如果 User 依赖 Order ,并且 Order 又反过来依赖 User ,那说明你们并没有把 User 跟 Order 解开。上面再套一层,还是没有解开,治标不治本。而且还是用毒药治那种,因为层是个很重的东西,加一层的成本是很高的。

相互依赖,一个原因是同层之间不同模块没解开造成的。对于你这个例子,根据用户 ID 获取所有订单信息,这实际上只是 Order 自身的事,跟 User 屁关系都没有。请注意用户 ID 作为不变的值,即使它是通过 User 生成的,它也不属于 User 而是属于全局,或者所有对它感兴趣的模块。

另一个原因,是根本没用好层造成的。分层既然隔离了技术,那自然要把基于技术的模块划分也给隔离了,不同层的模块划分原则是不一样的。还拿你这个例子来说,根据用户 ID 获取所有订单信息,在 UI 层往往是属于“我的……”这种用户个人资料模块的,但在业务逻辑层,或者数据模型层,它是属于 Order 模型的。
zhuangzhuang1988

zhuangzhuang1988      2 小时 37 分钟前

直接用 F#
编译都依赖文件顺序
vczyh

vczyh      2 小时 22 分钟前

@nothingistrue
情况 1:查询用户,带出对应的订单(造成 User 依赖 Order.getListByUserId(long userId)接口)
情况 2:查询订单,带出用户的某些信息(造成 Order 依赖 User.getSomeInfo(long userId))
请问这种 service 互相依赖怎么解决?
urnoob

urnoob      2 小时 15 分钟前

A 和 B 中有循环依赖,把循环依赖的部分放到 A 或者 B ,或者放进新创建的 C
xuanbg

xuanbg      1 小时 43 分钟前

@nothingistrue 是的,代码没写对地方,加多少层都没卵用。作为一个程序员,把代码写对地方才是第一重要的事。
lmshl

lmshl      1 小时 42 分钟前

笨方法:延迟初始化
普通方法:抽出一层
聪明方法:不要用类来组织它们,自然就不存在循环依赖了
Chad0000

Chad0000      1 小时 39 分钟前

@vczyh #37
@ChoateYao #30

同意 30 楼的,所有服务抽象成接口远程调用,不管你是订单依赖于用户还是用户依赖于订单,你依赖的东西全部都已经通过接口提供(实际为远程调用),除非你在两个服务中又分别调用对方的同一方法导致递归,就不存在依赖问题。

PS:我现在在公司就在梳理基于 Dapr 的微服务,现在已经将各服务抽象成接口了,调用方直接引用相应的接口库即可。这样你随便引用哪个接口库都无问题。
yrj

yrj      1 小时 36 分钟前

高度解耦,只依赖公共库或函数
lmshl

lmshl      1 小时 31 分钟前

比如我这个例子,UserService ,OrderService 都是静态存在的,唯一依赖是运行时的 Connection ,再怎么互相调用,再怎么交错也不会出错
amUjFxG.png
gfreezy

gfreezy      1 小时 29 分钟前

@vczyh Order 不返回 User 的信息,只返回 user_id 。Order 和 User 在上面加一层 UserOrder ,这一层根据 Order 的信息,从 User 中在获取对应 User 信息,最后拼接后返回。

但实际可能很多别的服务又依赖了 UserOrder 的返回结果,我们的方法是在这一层不要拆得太细,把相关的(依赖 UserOrder 的)都合并到一个模块,在很大程度上可以缓解这个问题。否则再加一层会非常复杂,得不偿失。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK