The class diagram
Lesson 3 of 6
3The class diagram
The diagram that literally becomes code and tables. Its most important part is not the rectangles but the numbers on the lines, we ran this one as written, the numbers proved themselves, and a bug in the order of the checks surfaced.
What the picture shows: Every number is a rule: a member up to five loans, a copy in at most one loan, a book many copies.
Map of this part
The concepts in order, click any one to jump to it:
Two ways into the same idea
A class diagram is like a family tree with the marriage contracts: who is linked to whom, and how many can be on each side. The small number on the line ("one" or "many") is the most important thing in the drawing, because it decides the shape of the tables and code later.
A class is a box with attributes and methods, and multiplicities 0..1, 1 and * decide foreign keys and join tables. composition means the part dies with the whole; aggregation means it moves. An association class appears when the link itself has data, like a loan date.
Required fundamentals
Parts one and two
The library's four rules and the nouns extracted from them: member, book, copy, loan.
One class in any language
You can write a class with fields and methods. We will turn the diagram into Python and run it.
Readiness for numbers
“1”, “0..1”, “0..5” and “*” are not decoration: each is a different business rule, and we will prove it by running code.
Core concepts
What things exist in this system, how do they connect, and in what numbers? No order, no time, this is a still photograph.
Three storeys: the name, then attributes (data), then operations (actions). The markers: + public, - private, # protected.
The number on the line *is* the business rule. “Copy → Loan” with 0..1 says a copy is with one member or with nobody. Write it 0..* and the system now allows one copy to be borrowed twice, one number, a different system.
When the relationship itself carries data, it becomes a class. A “Loan” is not a line between Member and Copy but a thing with a borrow date and a return date, the single most forgotten element in beginner models.
Association (plain line): two independent things, one knows the other. Aggregation (hollow diamond): a part that can move on. Composition (filled diamond): a part that dies with the whole. Inheritance (triangle arrow): “is a kind of”.
Ask “is X a kind of Y in every case?” If the answer is “sometimes”, the right answer is composition rather than inheritance. Models that fail usually inherit where they should have contained.
How it works
Diagram 1, Our library in four classes. The filled diamond says a copy is meaningless without a book
Diagram 2, Choosing the kind of line: three questions are enough
| Multiplicity | Reads as | In our example |
|---|---|---|
1 | Exactly one, mandatory | A copy belongs to exactly one book |
0..1 | Zero or one | A copy has an active loan, or none |
0..5 | Zero to five | A member's active loans |
1..* | At least one | If we required every book to have a copy |
0..* or * | Any number, zero included | A book's copies, rule four allows zero |
The exercise: execute the diagram, then break every rule in it
The diagram above is not a theoretical drawing: we wrote it in Python exactly as shown and ran it. Every multiplicity became a line of code, and every rule became a check:
class Book: # the bibliographic record
def __init__(self, isbn, title):
self.isbn, self.title = isbn, title
self.copies = [] # Book 1 --- 0..* Copy
def available(self):
return [c for c in self.copies if c.loan is None]
class Copy: # the physical thing on the shelf
def __init__(self, book, barcode):
self.book, self.barcode = book, barcode
self.loan = None # Copy 1 --- 0..1 Loan
book.copies.append(self)
class Loan: # the association class
def __init__(self, member, copy, today):
if copy.loan is not None:
raise ValueError("copy %s is already on loan" % copy.barcode)
if len(member.active()) >= MAX_LOANS:
raise ValueError("%s already holds %d copies" % (member.name, MAX_LOANS))
if member.overdue(today):
raise ValueError("%s is overdue and may not borrow" % member.name)
self.member, self.copy = member, copy
self.borrowed_on, self.due_on = today, today + timedelta(days=LOAN_DAYS)
self.returned_on = None
copy.loan = self
member.loans.append(self)0..* became a list inside Book, and 0..1 became a single field inside Copy. A list means “many”; a single field means “one or none”.Loan is not a line: it has three dates and a return method. Which is why it could hold all the checks.$ python3 library.py book 'Dune' has 7 copies, 7 available -- rule 2: one copy, one member at a time -- refused: copy D-01 is already on loan -- rule 3a: at most 5 copies -- Muath now holds 5 copies refused: Muath already holds 5 copies -- rule 3b: no borrowing while overdue -- overdue loans on 2026-03-21 : 5 refused: Muath already holds 5 copies <-- the wrong reason -- returning frees the copy, and the member -- copy D-01 free again: True borrowing works again: 1 active loan
Isolating rule 3b on a member with a single overdue loan produced the right message. And we tested rule four by exhausting every copy:
$ python3 order.py -- rule 3b isolated: overdue with only ONE loan -- active loans: 1 | overdue: 1 refused: Amal is overdue and may not borrow <-- correct here -- rule 4: exhaust every copy of a book -- copies: 3 | available: 0 | title still: Solaris
| In the diagram | In the code | In the database |
|---|---|---|
| Class | class | Table |
| Attribute | Field on the object | Column |
1 → 0..* | A list on the one side | A foreign key on the many side |
0..1 | One nullable field | A unique, nullable foreign key |
| Association class | A class with its dates | A junction table with extra columns |
| Composition (filled diamond) | The part is created by the whole | ON DELETE CASCADE |
Myth vs. reality
| The myth | The reality |
|---|---|
| “Multiplicity is cosmetic.” | Changing 0..1 to 0..* allows one copy to be borrowed twice. One number, a different system. |
| “A loan is just a line between Member and Copy.” | A line carries no date. The moment we needed a borrow date and a return date, the line became a class. |
| “Aggregation and composition are the same thing.” | Composition means the part dies with the whole (cascade delete); aggregation means it can move on. A different delete decision. |
| “Inheritance always reduces duplication.” | It does when “is a kind of” holds in every case. Otherwise it binds two unrelated classes by a false contract. |
| “A correct diagram means a correct system.” | Our diagram was correct, and running it revealed that the order of the checks gives the user a misleading reason. |
| “Draw every attribute and every method.” | Draw what answers the diagram's question. A complete list of thirty fields is neither read nor reviewed. |
Try it yourself
A copy is meaningless without its book, and is deleted with it. Which line?
“The part dies with the whole” is the definition of composition, and in a database it becomes a cascade delete. Aggregation means the part moves on to another whole, which is false here: a copy of “Dune” does not become a copy of another book.
You wrote 0..* between Member and Loan instead of 0..5. What breaks first?
Multiplicity *is* the rule. 0..* means “any number”, so the limit the librarian set disappears from the model, and you will not find a single line of code to remind you of it.
Quick review flashcards
0..1 means?1→0..* in a database?Check your understanding
1. In one sentence: why did “Loan” become a class rather than a line?
Because the relationship itself carries data: a borrow date, a due date and a return date. A line has nowhere to hold a value, so any relationship that needs even one field becomes a class, which is exactly what an “association class” is.
2. Explain in detail the effect of changing “Copy → Loan” from 0..1 to 0..*.
In the diagram the change looks like one character. In the code copy.loan turns from a single field into a list, so the check if copy.loan is not None falls away because it no longer means anything. The result is that two active loans can be recorded against the same copy, so two people borrow one physical book, something impossible in reality. And in the database the uniqueness constraint on the foreign key disappears. One rule vanishes from three layers because of one character.
3. (Spot the bug) A colleague made Member inherit from Book because “both have a name”. What is wrong?
Sharing an attribute is not an “is a kind of” relationship. A member is not a kind of book, and inheritance is a contract saying everything true of a book is true of a member, so isbn and copies would be inherited by Member for no reason. The right answer: no relationship at all, or, if the name recurs across many classes, an abstract base for what is genuinely shared, not for what merely looks alike.
4. The librarian says: “a rare book is not lent, it is read in the room only”. How do you model it?
Three options, each with a price. A field is_reference: bool on Copy: the simplest thing, and enough if the difference is a single rule. Inheritance, ReferenceCopy from Copy: conceptually right, but adds a class for a small behavioural difference. A reference type (a CopyType class): best if we expect other types with their own rules. The decision follows one question: will the number of types grow? If yes, the third; otherwise the first.
5. (Decision) The team wants to generate the code from the diagram automatically. Your answer?
That generation solves the easy part and leaves the hard one. Classes, fields and foreign keys really can be generated, but the order of the checks, the error messages and the alternative paths are not in the diagram at all, and we saw for ourselves that this is exactly where the bug was. More dangerous still, generated code creates a second copy of the truth: either the diagram is edited forever (doubling the work) or it rots (becoming misleading). Generate the skeleton once at the start if you like, and treat the code as the source from then on.
Resources for this part
Sorted by kind: start with the official docs when you want the precise reference, or with an article or video when you want the explanation first.
Resources for this lesson
Grouped by type. Start with the official docs for precision, or an article or video for the explanation.
Done with this lesson?
Sign in to save your progress.