封装
引入| English | 释义 | 拼音 |
|---|---|---|
| invariant/ɪnˈveərɪənt/ | 不变量 | bù biàn liàng |
| encapsulation/ɪnˌkæpsjʊˈleɪʃn/ | 封装 | fēng zhuāng |
| interface/ˈɪntəfeɪs/ | 接口 | jiē kǒu |
Out to two borrowers at once
- The library records say one copy is out to two borrowers at the same time. No method in the program can produce that state, so where did it come from?
- Someone reached past the methods and changed the data directly. This lesson is about building a wall that stops that.
Every class has an invariant
- An invariant 不变量 is a statement that is true between calls. For a library copy: it is either on the shelf or out to exactly one borrower, nothing else.
- Every method may break it for a moment inside, but must leave it true when it returns.
Encapsulation
- Encapsulation 封装 keeps the invariant true: the data is private and every change goes through methods that check it. Those public methods are the class's interface 接口.
- A leading underscore (
_out) marks internal data. It is a convention, not a lock, so the discipline is on the callers.
def issue(self):
if self._out:
raise ValueError('already out')
self._out = True
对已借出的副本再调 issue() 会——
难度 1/5 · 今日首次完成可获 100 金币及少量额外奖励
守卫在任何改动之前抛出——先检查意味着失败的调用不破坏任何状态。
In Python, a leading underscore stops other code from changing the attribute.
难度 1/5 · 今日首次完成可获 100 金币及少量额外奖励
It is only a convention that says "internal". Nothing stops a caller, so the discipline matters.
Why one gate is enough
- If every change to
_outgoes throughissueandgive_back, you can prove the invariant by reading just those two short methods. - Add one unchecked writer anywhere and the proof is gone: the places to check now grow with every caller.

Put the steps of a safe state-changing method in order.
难度 3/5 · 今日首次完成可获 200 金币及少量额外奖励
Checking first means a failed call leaves the object exactly as it was.
The cost of a leak
- A teammate writes
c._out = 'y': truthy, but notTrue. One caller (if c.is_out():) treats the copy as out; another (if c.is_out() is True:) treats it as available. - Two readers now disagree about one object. Fixing it means checking every reader; the check would have cost two
ifstatements.
在外部写 c._out = y 会让读者分裂,因为——
难度 1/5 · 今日首次完成可获 100 金币及少量额外奖励
y 为真但不是 True,于是 if is_out(): 与 if is_out() is True: 结论不同。
Expose behaviour, not storage
- Returning an internal list (
return self._loans) hands callers your private data to change. Return a copy instead. - Offer actions such as
available(), not a getter and setter for every field: get/set pairs for everything is not encapsulation.
直接返回内部列表有风险,因为——
难度 1/5 · 今日首次完成可获 100 金币及少量额外奖励
调用方拿到的正是你不变量所依赖的那个列表——应返回副本。
A library holds 3 loans and its loans() method returns self._loans. A caller appends 2 items to the list it got back. How many loans does the library now hold?
难度 2/5 · 今日首次完成可获 140 金币及少量额外奖励
The caller changed the library's own list. Returning list(self._loans) would have kept it at 3.
Match each line of code to what it does to encapsulation.
难度 2/5 · 今日首次完成可获 140 金币及少量额外奖励
Expose actions and copies; keep the real data behind the wall.
Worked example: the wall holds
c.issue()succeeds. A secondc.issue()raisesValueError, and the object is unchanged: the check comes first, so nothing needs undoing.c.give_back(), thenc.issue()works again._outis only everTrueorFalse.
Three common slips
- Returning the internal list. Callers can then change your private state.
- Checking in the constructor but not in the setter. Every way in needs the same check.
- Writing get/set pairs for everything. That publishes the storage; publish behaviour instead.
Recap
- An invariant is true between calls; encapsulation keeps it true.
- An underscore marks private data; public methods check every change at the gate.
- Check first so failed calls change nothing; return copies; expose behaviour, not fields.