Card 04/ 08
Composite Example: Pricing a Product Bundle
A bundle holds products and other bundles, and its price is the sum of what it holds.
A shop that sells bundles is a natural Composite, and this example maps the template onto it. A desk kit contains a keyboard, a mouse and a cable pack, and the cable pack is itself a bundle of two cables. The checkout needs a price for whatever is in the basket, and should not care whether it is one product or a bundle of bundles.
| Template | Bundle example |
|---|---|
| Component | Item, with price_pence |
| Leaf | Product, which returns its own price |
| Composite | Bundle, which sums the prices of its items |
| Client | show, which prints any item's price |
from abc import ABC, abstractmethod
class Item(ABC):
@abstractmethod
def price_pence(self) -> int: ...
class Product(Item):
def __init__(self, name: str, price_pence: int) -> None:
self.name = name
self._price_pence = price_pence
def price_pence(self) -> int:
return self._price_pence
class Bundle(Item):
def __init__(self, name: str, items: list[Item]) -> None:
self.name = name
self._items = items
def price_pence(self) -> int:
return sum(item.price_pence() for item in self._items)
def show(item: Item) -> None:
pence = item.price_pence()
print(f"{item.name}: £{pence // 100}.{pence % 100:02d}")
cables = Bundle("Cable pack", [Product("USB-C cable", 899), Product("HDMI cable", 1299)])
desk_kit = Bundle("Desk kit", [Product("Keyboard", 4500), Product("Mouse", 2000), cables])
show(Product("Mouse", 2000))
show(cables)
show(desk_kit)Mouse: £20.00
Cable pack: £21.98
Desk kit: £86.98The cable pack's £21.98 is the sum of its two cables. The desk kit's £86.98 is the keyboard, the mouse and the cable pack's total, so the recursion went one level deeper without any code saying so. Prices are held in pence, whole numbers, because a sum of floating-point pounds can come out a fraction of a penny wrong.
A rule that applies to bundles alone, such as ten per cent off any bundle, is a change to Bundle.price_pence. A new kind of item, such as a gift card with a fixed price, is a new leaf, and show and Bundle stay as they are.
← → move through sections and cards