From requirements to classes
Nouns, verbs and the use-case walk that finds the classes.
01The intuition
The problem it solves
The prompt says: "Design a library. Members borrow books, return them, and pay fines when late." A student who starts typing immediately writes one Library class with every list and every method in it, and a Book with a borrowed_by field. It works for the first two use cases. Then the interviewer says a library owns three copies of the same book, or a member may reserve a book that is out, and the single-class design has nowhere to put either fact without rewriting the methods that already work.
class Library:
def __init__(self) -> None:
self.books: list[str] = []
self.members: list[str] = []
self.loans: dict[str, str] = {} # title -> member
self.fines: dict[str, int] = {}
def borrow(self, title: str, member: str) -> None:
if title in self.loans: raise ValueError("out")
self.loans[title] = member # three copies? reservations? no room
Build it one piece at a time
Step one: before any code, write the use cases as short sentences with an actor and a verb: a member borrows a copy; a member returns a copy; the system charges a fine when a copy is late; a member reserves a book that has no free copy. Then underline the nouns: member, copy, book, fine, reservation, loan. Nouns that hold changing state or enforce a rule become classes; nouns that are just a value (a title, a date) become fields.
# use cases (written first, no code yet) # 1. member borrows a copy -> Member, Copy, Loan # 2. member returns a copy -> Loan ends, Copy free again # 3. late return charges a fine -> Fine belongs to Member, computed from Loan # 4. member reserves a book -> Reservation on Book, not on a Copy
Step two: for each noun, write the one sentence of responsibility and the few fields that follow from it. Book is the title record; Copy is a physical item that can be out; Loan ties a copy to a member with dates; the library is only the place these are looked up. Notice that "three copies" is now natural: one Book, three Copy objects.
from dataclasses import dataclass, field
from datetime import date
@dataclass
class Book:
isbn: str
title: str
copies: list["Copy"] = field(default_factory=list)
@dataclass
class Copy:
book: Book
barcode: str
loan: "Loan | None" = None # None means on the shelf
@dataclass
class Loan:
copy: Copy
member: "Member"
due: date
Step three: walk each use case through the classes and give the verb to the object that owns the data it changes. Borrowing changes a copy and creates a loan, so it lives where copies are found; the fine is computed from a loan, so the loan knows how late it is.
class Member:
def __init__(self, name: str) -> None:
self.name = name
self.loans: list[Loan] = []
self.fines: int = 0
class Loan:
def days_late(self, today: date) -> int:
return max(0, (today - self.due).days)
class Catalogue:
def borrow(self, book: Book, member: Member, today: date) -> Loan:
free = next((c for c in book.copies if c.loan is None), None)
if free is None:
raise NoCopyFree(book.isbn)
loan = Loan(free, member, today + LOAN_PERIOD)
free.loan = loan; member.loans.append(loan)
return loan
Why it works
The principle: classes come from the problem, not from the solution. Each use case is a path through a few objects, and a design is good when every path reads naturally and every rule has exactly one home. The invariant to check is that each piece of state is owned by one class and changed through its methods only. Where it breaks: a class named after a verb (BorrowManager) that holds no state, a noun turned into a class when it was only a value (Title), and the "system" noun that collects every method because nobody asked who owns what. Interviewers test by adding a requirement mid-way; if it lands in one class with a small change, the nouns were right.
The trade-off you must say out loud
Spending five of forty-five minutes on sentences feels slow, and it is possible to over-model: not every noun deserves a class, and a reservation might be a list of members on the book until a rule needs more. Say that you start with the fewest classes the use cases need and let the interviewer's extensions pull new ones out. The usual follow-up is a new use case; answer it by walking it through the existing objects before adding anything.
Where it shows up in the 100 Google problems
- Library management system: the example above with holds and fines completed.
- Parking lot: lot, floor, spot, vehicle, ticket come straight out of the park-and-leave use cases.
- Restaurant waitlist: party, table and the waitlist itself; the use cases decide whether a party or a person is the unit.
- Splitwise: group, expense, share and balance, where the balance is derived rather than stored.
- Hotel booking system: room type versus room, the same split as book versus copy.
02Used in these problems
- Design a parking lot
- Design an elevator controller
- Design a restaurant waitlist
- Design a restaurant management system
- Design a movie ticket booking system
- Design a hotel booking system
- Design an airline reservation system
- Design a car rental system
- Design a student-to-room allocation system
- Design a library management system
- Design Snake and Ladder
- Design Monopoly
- Design Splitwise
- Design an ATM
- Design an online stock brokerage
- Design a vending machine
- Design an online shopping system
- Design a food delivery system
- Design an inventory management system
- Design a task management system (Trello-like)
- Design a Twitter feed (class level)
- Design Stack Overflow