下一讲 — 第六模块:接口与契约
抽象基类作为接口、Python Protocol结构子类型、契约式设计,以及SysML v2的interface def映射。
由对象构建对象——生命周期所有权与共享引用、UML实心/空心菱形符号与SysML v2的part vs ref。
has-a关系的两种形式,具有截然不同的生命周期语义:
| 关系 | 含义与生命周期 |
|---|---|
| 组合 ◆ | 强所有权。子对象在父对象内部创建,与父对象共存亡。删除父对象会删除子对象。 |
| 聚合 ◇ | 弱引用。子对象独立存在,可从多处共享引用。删除容器不影响被引用对象。 |
Order在内部创建OrderItem和Payment——它们在Order之外没有任何意义:
class Order:
def __init__(self, order_id, customer):
self.order_id=order_id; self._status="draft"
self._items: list[OrderItem] = [] # 组合:在此创建
self._payment: Payment | None = None
def add_item(self, book, qty=1):
self._items.append(OrderItem(book, qty, book.price))
def place(self, method):
self._payment = Payment("PAY-"+self.order_id, self.total(), method)
self._payment.process(); self._status="placed"
def cancel(self):
if self._payment: self._payment.refund()
self._status="cancelled" # _items和_payment随后被垃圾回收ShoppingCart和Inventory持有Book的引用但不拥有所有权。同一个Book对象可以同时被多个容器引用:
class ShoppingCart:
def __init__(self, cart_id):
self._items: list[Book] = [] # 聚合:外部引用
def add_item(self, book): self._items.append(book)
def remove_item(self, book): self._items.remove(book) # book依然存在
def to_order(self, order_id, customer):
order = Order(order_id, customer)
for book in self._items: order.add_item(book)
self._items.clear() # 购物车清空,书本身仍在其他地方存在
return order| 场景 | 正确关系 |
|---|---|
| "DigitalBook是一种Book" | 继承 ✔ — 明确且永久成立 |
| "ShoppingCart拥有Book" | 聚合 ✔ — 购物车不是一种Book |
| "Order拥有Payment" | 组合 ✔ — 付款是订单的组成部分 |
| "带日志功能的Customer" | 组合或装饰器 ✔ — 不是新的Customer类型 |
// 组合:Order拥有其子对象
part def Order {
part items : OrderItem[1..*]; // 拥有的生命周期
part payment : Payment; // 拥有的生命周期
}
// 聚合:ShoppingCart引用共享的Book
part def ShoppingCart {
ref items : Book[0..*]; // 不拥有——共享引用
}part x : ChildClassref x : SomeTypepart关键字(拥有的生命周期)ref关键字(共享引用)强所有权。子对象随所有者创建和销毁。Order拥有OrderItem和Payment。
共享引用。对象独立存在,可从多处共享。
删除容器=销毁子对象?是=组合。否=聚合。
is-a不明确时优先选择组合。产生更少耦合,更易于更改。
组合=内部构建。聚合=接受参数或引用现有对象。
part=拥有生命周期。ref=共享引用。
抽象基类作为接口、Python Protocol结构子类型、契约式设计,以及SysML v2的interface def映射。