解决循环引用并演进中介者模式需四步:先提取共享契约、接口抽象与延迟导入以切断依赖闭环;再识别协同业务事件;接着构建只调度不决策的轻量中介者;最后通过新增/屏蔽/测试验证解耦效果。
解决模块化项目中的循环引用,同时向中介者模式演进,本质是做两件事:先打破编译期/导入期的依赖闭环,再把运行时的交互逻辑从对象网状调用中抽离出来,集中到一个协调点。这不是单纯“加个中介类”,而是结构升级——从互相喊话,变成统一听指挥。
第一步:切断循环引用的物理链路
循环引用是重构的前提障碍,不解决就无法安全引入中介者。关键不是绕过它,而是让依赖方向清晰、单向:
把多个模块共用的类型、接口、事件定义提取到独立包(如
shared
或
contracts
),原模块只依赖它,不再彼此 import
用接口替代具体实现:A 模块定义
接口,B 模块实现它;A 通过参数或构造函数接收该接口,不 import B 的包
在 FastAPI 或类似框架中,避免顶层直接 import app 实例;改用依赖注入,比如
由路由函数按需声明,而非在 models.py 里写
对 Python/JS 中难以拆分的场景,启用延迟导入——把
放进函数体内,仅在真正需要时加载
第二步:识别并收敛交互逻辑
循环引用往往掩盖着更深层的问题:多个模块正在协同完成同一业务流程,却没人统筹。这时要问:
哪些操作总是一起发生?比如“下单”必然触发“扣库存”+“发通知”+“更新用户积分”
哪个模块每次修改,都迫使其他三四个模块跟着改?这说明协调逻辑散落各处
新增一个动作(如加“发送企业微信消息”)是否要改订单、库存、通知三个文件?那正是中介者该接管的地方
把这些隐含的协作规则显性化,整理成事件清单,例如:
、
、
。
第三步:构建轻量中介者,守住边界
中介者不是新业务层,而是调度中枢。它的职责非常明确:
只持有同事类的接口引用,不持有具体实现类;所有业务逻辑仍留在原服务中(如库存校验仍在
)
方法只做三件事:接收事件 → 调用对应服务 → 触发下游事件;不处理 if-else 分支逻辑,复杂分支用策略模式外置
禁止跨系统直连:订单服务通知物流,不应走内存中介者,而应发 MQ 消息;中介者只管本进程内协调
同事类之间严禁私聊:代码审查时可加入规则,禁止出现
这类跨模块直调语句
第四步:验证重构是否真正生效
成功与否,不看中介者类多大,而看日常开发是否变简单:
想新增“下单后生成优惠券”?只需在中介者中加一行
,不动订单、用户、支付任一原有模块
测试订单创建流程?Mock 一个空的
,断言它是否按顺序调用了库存检查、通知发送、积分更新这三个方法
临时禁用短信?注释掉中介者里
那一行即可,其余流程照常运行
当新增、屏蔽、测试都变得局部且可控,说明变量耦合已解除,中介者模型真正落地了。
Notifierdef get_db(): ...from main import appimportonOrderCreatedonStockShortageonPaymentConfirmedStockServiceuserSvc.updateProfile()couponService.generate(order)MediatorsmsSender.send()