ooad1-5 Exercise 2: From Code to Diagram ============================================== shop_code.py (the code you were given; Python 3.14): from dataclasses import dataclass, field from typing import Protocol class Pricing(Protocol): def price(self, hours: float, parts: list["JobPart"]) -> int: ... @dataclass class Part: part_no: str price_pence: int @dataclass class JobPart: part: Part quantity: int @dataclass class Bike: description: str class StandardPricing: def price(self, hours: float, parts: list[JobPart]) -> int: return int(hours * 3000) + sum(p.part.price_pence * p.quantity for p in parts) @dataclass class RepairJob: bike: Bike hours: float = 0.0 parts: list[JobPart] = field(default_factory=list) def add_part(self, part: Part, quantity: int) -> JobPart: used = JobPart(part, quantity) self.parts.append(used) return used def invoice(self, pricing: Pricing) -> int: return pricing.price(self.hours, self.parts) if __name__ == "__main__": job = RepairJob(Bike("red road bike"), hours=1.5) job.add_part(Part("TUBE-700", 450), 2) print("invoice (pence):", job.invoice(StandardPricing())) Run with: py shop_code.py Output: invoice (pence): 5400 shop_answer.class.mmd (Mermaid, rendered with Mermaid 12.1.0): classDiagram direction LR class Pricing { <> +price(hours, parts) int } class StandardPricing class RepairJob { +hours +add_part(part, quantity) JobPart +invoice(pricing) int } class JobPart { +quantity } class Part { +part_no +price_pence } class Bike { +description } StandardPricing ..|> Pricing RepairJob ..> Pricing RepairJob "1" *-- "0..*" JobPart JobPart "*" --> "1" Part RepairJob "*" --> "1" Bike HOW EACH LINK WAS DECIDED StandardPricing ..|> Pricing Realisation: StandardPricing has the method price(hours, parts) that the Pricing Protocol requires, and does not inherit from it. RepairJob ..> Pricing Dependency: invoice(pricing) takes it as a parameter and stores nothing. RepairJob *-- JobPart Composition: JobPart objects are created inside the job (add_part) and live in the job's own list; a JobPart is meaningless without its job. JobPart --> Part Association: a JobPart holds a Part (a field), but a Part exists independently of any job. RepairJob --> Bike Association: RepairJob holds a Bike (a field); a Bike does not know its jobs, so the arrow is one way. The 5400 pence in the output is 1.5 hours at 3000p plus two tubes at 450p: 4500 + 900. WHY THIS WORKS AS AN ANSWER --------------------------- The test for each link is what the code does: a field means association, creation inside the owner means composition, a parameter means dependency, a matching method set means realisation. The diagram omits private detail (dataclass boilerplate, the default list factory) because the reader does not need it.