Ran Wei /Object-Oriented Design/Module 5
中文
Object-Oriented Design — Ran Wei

Module 5: Composition & Aggregation

Building objects from objects — lifecycle ownership vs shared references, filled/open diamond UML, and SysML v2 part vs ref.

Prereq: OOD Module 4Python 3UML 2.5SysML v2
1

Composition vs Aggregation

Modules 1–4 explored is-a hierarchies. This module explores the has-a relationship: two forms with very different lifecycle semantics.

RelationshipMeaning & Lifetime
Composition ◆Strong ownership. Child created inside parent. Destroying the parent destroys the children. Order ◆→ OrderItem.
Aggregation ◇Shared reference. Child exists independently, may be referenced from many containers simultaneously. Cart ◇→ Book.
ℹ️ KEY TEST
If you delete the container, should the contained objects also be destroyed? Yes → composition. No (they exist independently) → aggregation.
2

Composition in Practice

Order composes OrderItem and Payment — they have no meaning outside their Order:

Python
class Order:
    def __init__(self, order_id, customer):
        self.order_id=order_id; self._status="draft"
        self._items: list[OrderItem] = []   # composed: created here
        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 and _payment garbage-collected
diagram
Figure 1 — Filled diamonds: Order owns OrderItem (1..*) and Payment (1). The dashed arrow from OrderItem to Book is aggregation — the Book lives in Inventory independently.
3

Aggregation in Practice

ShoppingCart and Inventory hold references to Books they do not own. The same Book object is shared between them:

Python
class ShoppingCart:
    def __init__(self, cart_id):
        self._items: list[Book] = []   # aggregation: external references

    def add_item(self, book): self._items.append(book)
    def remove_item(self, book): self._items.remove(book)  # book still exists
    def to_order(self, order_id, customer):
        order = Order(order_id, customer)
        for book in self._items: order.add_item(book)
        self._items.clear()   # cart emptied; books alive elsewhere
        return order

# Both containers reference the SAME object
assert inventory.get_book("978-1-11") is cart._items[0]  # True
diagram
Figure 2 — Open diamonds: ShoppingCart and Inventory both reference the same Book objects without owning them.
💡 SHARED IDENTITY
Both containers hold a reference to the exact same Python object. When Inventory updates a Book's stock, carts holding that Book see the change immediately.
4

Composition vs Inheritance

ScenarioCorrect relationship
"A DigitalBook is a Book"Inheritance ✔ — clearly true
"A ShoppingCart has Books"Aggregation ✔ — cart is not a kind of Book
"An Order has a Payment"Composition ✔ — payment is part of the order
"A logging Customer"Composition or Decorator ✔ — not a new customer type
⚠️ Pitfall
Favour composition over inheritance. If is-a is not clearly and permanently true, use composition. Deep hierarchies are brittle — a parent change ripples through all subclasses.
5

SysML v2 Bridge

SysML v2
// Composition: Order owns its children
part def Order {
    part items   : OrderItem[1..*];  // owned lifecycle
    part payment : Payment;          // owned lifecycle
}
// Aggregation: ShoppingCart references shared Books
part def ShoppingCart {
    ref items : Book[0..*];          // NOT owned - shared reference
}
Python / OOD
SysML v2
Composition (internal creation)
part x : ChildClass
Aggregation (external reference)
ref x : SomeType
UML filled diamond ◆
part keyword (owned lifecycle)
UML open diamond ◇
ref keyword (shared reference)
6

Summary

Composition ◆

Strong ownership. Child created and destroyed with owner. Order owns OrderItem and Payment.

Aggregation ◇

Shared reference. Object exists independently, may be shared from many places.

Key Test

Delete container = destroy children? Yes = composition. No = aggregation.

vs Inheritance

Favour composition when is-a is not clearly true. Less coupling, easier to change.

Python

Composition = construct internally. Aggregation = accept as parameter.

SysML v2

part = owned lifecycle. ref = shared reference.

📚
Next — Module 6: Interfaces & Contracts

ABCs as interfaces, Python Protocol for structural subtyping, Design by Contract, and SysML v2 interface def.