Class Diagrams
A class diagram is a static picture of your system: the boxes represent classes, the lines between them represent relationships, and the text inside each box lists the fields and methods the class owns. In an LLD interview you will draw or talk through a class diagram before writing any code, because it forces you to name your entities and their connections before committing to implementation details. Getting the diagram right shortens the coding phase significantly.
Anatomy of a class box
Each class is a rectangle with three compartments. The top compartment holds the class name. The
middle holds fields (attributes), and the bottom holds methods (operations). Visibility markers
prefix each member: + for public, - for private, # for protected. In a whiteboard session
you can omit the third compartment until you need it — showing fields and relationships is usually
enough to convey the design.
An e-commerce model
Consider an e-commerce checkout: customers place orders, each order contains line items, and each
line item references a product. Those four nouns — Customer, Order, LineItem, Product —
are your entities. Drawing their relationships before writing any code reveals the ownership
structure immediately.
The filled diamond on Order *-- LineItem signals composition: line items are created by the
order and deleted with it. The plain arrows on the other links signal looser coupling — Customer
places many orders but those orders could, in principle, exist independently. The multiplicity
labels (1, many) communicate cardinality without a word of prose.
Reading multiplicity
Multiplicity sits at both ends of a relationship line. 1 means exactly one; many (or *)
means zero or more; 1..* means at least one. In the e-commerce diagram, Customer "1" --> "many" Order reads: one customer places zero or more orders. LineItem "many" --> "1" Product reads:
many line items can reference the same product, but each line item references exactly one product.
Getting multiplicity right prevents implementation bugs. If you draw Order "1" *-- "1" LineItem
you are saying an order has exactly one line item — clearly wrong. Multiplicity on the diagram
forces you to confront edge cases before you write the List vs. single-field decision in code.
Inheritance and interfaces
Two other arrow types appear in class diagrams: the hollow triangle (<|--) marks inheritance
("is-a"), and the dashed hollow triangle (<|..) marks interface implementation ("implements").
1from abc import ABC, abstractmethod
2from enum import Enum, auto
3
4class OrderStatus(Enum):
5 PENDING = auto()
6 CONFIRMED = auto()
7 SHIPPED = auto()
8 DELIVERED = auto()
9
10class Customer:
11 def __init__(self, customer_id: str, email: str):
12 self._customer_id = customer_id
13 self._email = email
14 self._orders: list["Order"] = []
15
16 def place_order(self, items: list[tuple[str, int]]) -> "Order":
17 order = Order(customer_id=self._customer_id)
18 for product_id, qty in items:
19 order.add_item(product_id, qty)
20 self._orders.append(order)
21 return order
22
23class Order:
24 _counter = 0
25
26 def __init__(self, customer_id: str):
27 Order._counter += 1
28 self._order_id = str(Order._counter)
29 self._customer_id = customer_id
30 self._status = OrderStatus.PENDING
31 self._items: list["LineItem"] = [] # composition
32
33 def add_item(self, product_id: str, qty: int) -> None:
34 # In a real system, look up unit_price from a Product repository
35 self._items.append(LineItem(product_id, qty, unit_price=9.99))
36
37 @property
38 def total(self) -> float:
39 return sum(item.subtotal for item in self._items)
40
41class LineItem:
42 def __init__(self, product_id: str, quantity: int, unit_price: float):
43 self.product_id = product_id
44 self.quantity = quantity
45 self.unit_price = unit_price
46
47 @property
48 def subtotal(self) -> float:
49 return self.quantity * self.unit_price