📄 一句话概括
循环依赖的可怕在于环里的任何一个都不能单独拿走——实测 5 个类 5 条依赖里检测出 1 个环:Item → Price → Item。
📝 本节和前面几节的关系
10.4~10.5 讲的是【依赖多少】,这一节讲依赖的形状——有环就麻烦。
一、循环依赖长什么样
A 用 B、B 用 C、C 又用 A——这就是环。
最小的是两两互用:Item 需要 Price 算价,Price 需要 Item 取类型。
实测 5 个类 5 条依赖里,正好 1 个这样的环。
二、为什么环这么危险
环里的任何一个都不能单独拿走——你抽 Item,Price 就编译不过。 它们被绑成一个整体,而这个整体会越来越大。 改的时候也不安全:改 Item 可能影响 Price,改 Price 又影响 Item,测都测不清。
三、三种破环办法
上移:把互相需要的那部分抽到一个新的类,两边都依赖它。 下移:把其中一个的职责再拆,让依赖变成单向。 只依赖接口:一方只看抽象,实现由外部注入——环就断在抽象这一层。
四、怎么发现环
人眼看三个以上的环很难,所以用工具:导入时报警、静态扫描、构建期检查。 简单办法:数一数有没有哪个模块【既被别人 import 又 import 别人】。 最好的办法是让它不可能出现:分层,只允许上层依赖下层。
五、动手 Demo:设计目标之三:避免依赖关系发生循环
用邻接表描述 5 个类的依赖。 用三色 DFS 检测环,并打印环的路径。
edges = {"Order": ["Customer", "Item"],
"Customer": ["Address"],
"Item": ["Price"],
"Price": ["Item"],
"Address": []}
cycles, color, stack = [], {}, []
def dfs(n):
color[n] = 1
stack.append(n)
for m in edges.get(n, []):
if color.get(m) == 1:
cycles.append(stack[stack.index(m):] + [m])
elif color.get(m, 0) == 0:
dfs(m)
stack.pop()
color[n] = 2
for n in edges:
if color.get(n, 0) == 0:
dfs(n)
print(len(edges), sum(len(v) for v in edges.values()), len(cycles))
# 5 5 1
实跑输出:
--------------------------------------------------------------------
设计目标之三:避免依赖关系发生循环
--------------------------------------------------------------------
循环依赖的表现是:**编译/导入时报错,或者改哪个都不敢动。**实测一个依赖图:
类 依赖谁
----------------------------
Order Customer/Item
Customer Address
Item Price
Price Item
Address —
类 5 个
依赖边 5 条
检测到环 1 个
环的路径 Item → Price → Item
**环只有 1 个:Item 与 Price 互相依赖。**
**破环的办法**:把 Price 需要的那部分抽成第三方,**或者让一方只看接口。**
**为什么环这么危险**:环里的任何一个都【不能单独拿走】——**它们被绑成一个整体。**
实测:5 个类、5 条依赖、1 个环(Item → Price → Item)。 环里的两个类不能单独拿走——它们被绑成一个整体。 破环:上移、下移、或者只依赖接口。
30 秒实验
- 画一张你项目的依赖图,找出所有互相 import 的地方。
- 挑一个环,试着用【只依赖接口】断开它。
常见误区
- 以为循环依赖只是编译慢。它让环里的东西无法单独拿走。
- 靠人眼找环。三个以上的环人眼看不出,要靠工具。
自测三问(点开对答案)
1. 循环依赖为什么危险? —— 环内任一模块都不能单独拿走,它们被绑成一个整体,改一处影响不清。 2. 实测检测结果如何? —— 5 个类 5 条依赖里 1 个环:Item → Price → Item。 3. 三种破环办法? —— 上移(抽新类)、下移(再拆职责)、只依赖接口(由外部注入实现)。- ☐ 10.7 面向对象设计的【感觉】是拟人化和职责分配
相关笔记
- 提高构件独立性的诀窍 — 减少依赖的手法
- 进化的OOP结构之一:包 — 分层与包的关系
- 面向对象设计的【感觉】是拟人化和职责分配 — 从结构走到责任
- 🧩 面向对象是怎样工作的 · 索引