Final Capstone: A Community Lending Library · practice
Draw the First Object Collaboration
Before writing the interesting
One diagram, four boxes
Library
| owns the catalog -> Book, Game
| owns the members -> Member
| owns the active loans -> Loan
Every arrow points the same way, and that is the design. Library knows about items, members, and loans. None of them knows about Library.
Chapter 6 gave the reason: a required dependency back the other way makes the Member required a library in its constructor, a test of member validation would have to supply one too. This project has no member operation that needs that dependency.

The Chapter 10 word for this shape is a dependency direction, and keeping it one-way is most of what makes a small system stay workable.
Owning a collection means owning the rules about it
Library holds three collections. Holding them is not the interesting part; deciding what may go into them is.
Two rules apply from the start, and both come out of the contract:
duplicate item and member identifiers are rejected
An id has to mean exactly one thing, or checkout("b1", ...) is ambiguous and the whole contract falls apart. So add_item and register_member are not blind appends. They are the gatekeepers for their collections.
That makes them the first real methods worth writing, and the first place the seeded errors get used:
from errors import LibraryError
def add_item(self, item):
if item.item_id in self._items:
raise LibraryError(f"item {item.item_id!r} is already in the catalog")
...
Chapter 12’s
Why a dictionary keyed by id
The contract hands you identifiers. checkout(item_id, member_id, checkout_day) gets a
A
self._items = {}
self._members = {}
self._loans = {}
The leading underscore is Chapter 5’s convention. These are the library’s own bookkeeping, not part of what it promises. Lesson 6 is where they get exposed carefully, through methods that hand out copies.
Why does Member not hold a reference to the Library it belongs to?
What to build now
Give Library its three collections in __init__, and turn register_member and add_item into real methods that add to them and reject a repeated id.
The other five stay as they are. A capstone gets built in working order, and there is nothing wrong with a class that is half placeholder as long as you can say which half.
Task
Give Library its collections and its two gatekeepers, in library.py.
__init__ takes nothing but self and sets up three empty
add_item(item) stores the item under its item_id, and raises LibraryError when that id is already in the catalog.
register_member(member) does the same for member.member_id.
Import LibraryError from errors. Give each refusal a message naming the id that clashed.
Leave the other five NotImplementedError.