Lesson 1 of 109 · Module 1: Classes, objects and responsibilities · about 25 min theory + exercises
1. Learning Objective
A. What you will learn
By the end of this lesson you should be able to:
- i. Explain what Low Level Design (LLD) is.
- ii. Explain how LLD differs from High Level Design (HLD).
- iii. Describe a small piece of software as objects with responsibilities.
B. Why it matters
- i. Every later lesson (SOLID, patterns, Parking Lot, Splitwise) builds on this way of thinking.
- ii. Interviews test whether you can reason from requirements, not whether you remember code.
C. What you will build
- i. A
BankAccountthat protects its own rules. - ii. A
ShoppingCart(Exercise 1). - iii. A discount calculator using a first pattern, Strategy (Exercise 2).
D. Prerequisites
You should already understand classes, methods, fields and constructors. Examples use Java.
2. The Problem
A. The situation
Your team is told: "Build a banking app." Two engineers start.
- i. Engineer 1 draws boxes: mobile app, API gateway, database, cache, queue.
- ii. Engineer 2 opens an editor and asks: what classes exist, who may change a balance, what happens on an overdraw?
B. The observation
Both are designing, but at different levels. Engineer 1 is doing HLD. Engineer 2 is doing LLD.
C. The question this lesson answers
What exactly is LLD, and what does "thinking in objects" mean?
D. Why a beginner gets stuck
- i. A beginner sees only data (
accountNumber,balance) and functions that modify it. - ii. Nobody owns the rules, so the rules end up scattered across the code.
3. Concept: HLD vs LLD
A. Side-by-side comparison
| High Level Design (HLD) | Low Level Design (LLD) | |
|---|---|---|
| Zoom level | Whole system | Inside one service or module |
| Building blocks | Services, databases, queues, caches | Classes, interfaces, objects, methods |
| Core question | What are the big parts and how do they communicate? | What are the objects, who owns what, how do they collaborate? |
| Typical output | Architecture diagram | Class model, interfaces, code structure |
B. One example, two levels
HLD → "We need an Account Service and a Notification Service."
LLD → "Inside Account Service: Account, Transaction, Money, AccountRepository..."
C. What LLD decides
- i. Which objects exist.
- ii. What each object knows (state) and does (behavior).
- iii. Who is responsible for what.
- iv. How objects depend on and talk to each other.
- v. How easily the design absorbs a changing requirement.
D. Thinking in objects
- i. Data thinking:
accountNumber = "ACC-1",balance = 1200, with functions that change them from anywhere. - ii. Object thinking: one
BankAccountthat owns its data and protects its own rules.
BankAccount
state: accountNumber, balance
behavior: deposit(), withdraw(), getBalance()
rule: balance can never go negative
- iii. The shift: data and the rules about that data live together.
4. Bad Design
A. The naive class
Anyone can reach in and change the balance.
public class BankAccount {
public String accountNumber;
public double balance;
}
B. How it gets misused
account.balance = account.balance - 5000; // overdrawn, nobody stopped it
account.balance = -999; // nonsense state, nobody stopped it
C. Why it is bad
- i. The rule "balance cannot go negative" is written nowhere. Every caller must remember it.
- ii. If the rule changes (say, an overdraft limit), you must hunt down every place that touches
balance. - iii. The class is a bag of data with no responsibilities, so responsibility scatters across the codebase.
D. The key question
Who should enforce the rules of an account? The object that owns the data.
5. Better Design
A. The improved class
public class BankAccount {
private final String accountNumber;
private double balance;
public BankAccount(String accountNumber, double openingBalance) {
if (openingBalance < 0) {
throw new IllegalArgumentException("Opening balance cannot be negative");
}
this.accountNumber = accountNumber;
this.balance = openingBalance;
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit must be positive");
}
balance += amount;
}
public void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Withdrawal must be positive");
}
if (amount > balance) {
throw new IllegalStateException("Insufficient funds");
}
balance -= amount;
}
public double getBalance() { return balance; }
public String getAccountNumber() { return accountNumber; }
}
B. What changed
- i. Fields are
private, so outside code cannot touch them. - ii. State changes only through
depositandwithdraw. - iii. The constructor refuses an invalid starting state.
C. The result
- i. The withdrawal rule lives in exactly one place.
- ii. It is impossible to put the account into an invalid state from outside.
- iii. This idea is called encapsulation. We study it properly in Lesson 4.
D. How we arrived at this design
Ask these questions, in this order:
What object is this? → BankAccount
What does it know? → accountNumber, balance
What can it do? → deposit, withdraw
What rules must always hold? → balance >= 0, amounts > 0
Who should enforce those rules? → the object that owns the data
6. Small Exercise 1: Design Before You Code
A. Requirement
A ShoppingCart holds items. A customer can add an item, remove an item, and see the total price.
B. Step one: design on paper, no code yet
Fill in this table:
| Question | Your answer |
|---|---|
| What objects exist? | |
| What does each object know (state)? | |
| What can each object do (behavior)? | |
| Who owns the list of items? | |
| Who calculates the total? | |
| What rule must always hold? |
C. Step two: implement
- i. Write
CartItemandShoppingCartin Java. - ii. Keep fields private.
- iii. Put validation inside the classes, not in the callers.
D. Self-check
- i. Can outside code add a negative quantity?
- ii. Can outside code edit the item list directly? (Try returning an unmodifiable view.)
- iii. If the total calculation changes, how many classes do you edit?
7. Introduction to a Design Pattern: Strategy (Preview)
Note: your roadmap teaches patterns in Week 5, after learners have felt the problems they solve. Today is a preview only. Do not memorize it. We return to Strategy in Lesson 40, and again in Discount Engine, Parking Lot and Payment System.
A. The problem
The bank now pays monthly interest. Savings accounts get 4% a year, student accounts 5%, current accounts 0%.
Naive solution:
public double monthlyInterest(double balance, String accountType) {
if (accountType.equals("SAVINGS")) {
return balance * 0.04 / 12;
} else if (accountType.equals("STUDENT")) {
return balance * 0.05 / 12;
} else if (accountType.equals("CURRENT")) {
return 0;
}
throw new IllegalArgumentException("Unknown account type");
}
B. Why the naive solution breaks
Marketing launches a "Senior Citizen" account.
- i. You must open and edit working code. Every edit risks breaking the existing cases.
- ii. Every new type grows the
if/elsechain. - iii. The logic depends on a string, so a typo like
"SAVNGS"is only caught at runtime. - iv. You cannot test one interest rule in isolation.
The LLD question: what is changing? Only the way interest is calculated. Deposit, balance and account number stay still.
C. The pattern
Strategy: pull the part that varies into its own interface. The object holds an implementation and delegates to it.
InterestStrategy (interface)
↑
┌───────────┼───────────┐
SavingsInterest StudentInterest CurrentInterest
public interface InterestStrategy {
double monthlyInterest(double balance);
}
public class SavingsInterest implements InterestStrategy {
public double monthlyInterest(double balance) { return balance * 0.04 / 12; }
}
public class StudentInterest implements InterestStrategy {
public double monthlyInterest(double balance) { return balance * 0.05 / 12; }
}
public class CurrentInterest implements InterestStrategy {
public double monthlyInterest(double balance) { return 0; }
}
The account holds a strategy and delegates:
public class InterestBearingAccount {
private final String accountNumber;
private double balance;
private final InterestStrategy interestStrategy;
public InterestBearingAccount(String accountNumber, double openingBalance,
InterestStrategy interestStrategy) {
if (openingBalance < 0) {
throw new IllegalArgumentException("Opening balance cannot be negative");
}
this.accountNumber = accountNumber;
this.balance = openingBalance;
this.interestStrategy = interestStrategy;
}
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException("Deposit must be positive");
balance += amount;
}
public double monthlyInterest() {
return interestStrategy.monthlyInterest(balance);
}
public double getBalance() { return balance; }
}
Using it:
InterestBearingAccount savings = new InterestBearingAccount("S-1", 12000, new SavingsInterest());
InterestBearingAccount current = new InterestBearingAccount("C-1", 12000, new CurrentInterest());
// Senior citizen account: no existing class edited, just a new strategy.
InterestBearingAccount senior =
new InterestBearingAccount("U-1", 12000, balance -> balance * 0.06 / 12);
System.out.println(savings.monthlyInterest()); // 40.0
System.out.println(current.monthlyInterest()); // 0.0
System.out.println(senior.monthlyInterest()); // 60.0
Adding a new account type now means adding code, not editing working code.
D. When to use it, when not to, and how to spot it
- i. Use it when one job can be done in several interchangeable ways and new ways keep appearing.
- ii. Do not use it when there is one way and it is unlikely to change. With a single interest rule, an interface plus class is overengineering (YAGNI and KISS, covered in Week 3).
- iii. Recognition clues: a growing
if / else iforswitchon a type or mode, the phrase "depending on X, calculate it differently", or needing to swap behavior at runtime or in tests. - iv. Real-world examples: Java's
Comparator, choosing card vs UPI at checkout, choosing the route type in a maps app.
8. Small Exercise 2: Discount Strategy
A. Start with the naive version
Write a Cart with double finalPrice(double total, String customerType) using an if/else chain:
- i.
REGULARpays full price. - ii.
MEMBERgets 10% off. - iii.
FESTIVALgets 20% off.
B. Feel the pain
Add a fourth type, STAFF (30% off). Note exactly what you had to edit.
C. Refactor to Strategy
- i. Create a
DiscountStrategyinterface withapply(double total). - ii. Create one implementation per customer type.
- iii. Make
Carthold aDiscountStrategyand delegate to it.
D. Prove the design works
- i. Add
FIRST_ORDER(flat 100 off) without editing theCartclass. - ii. Success check:
Cartcontains noifon customer type.
9. Can You Explain?
A. HLD vs LLD
In your own words, what is the difference? Give one example decision from each.
B. Encapsulation effect
Why does BankAccount with a private balance and a withdraw method beat a class with a public balance field?
C. What is changing?
In the naive interest code, what is changing, and why does that question point toward Strategy?
D. Self-test rule
Answer all three without looking at the lesson. Note which ones you could not answer.
10. Can You Design?
A. Given
A Library lends books to members.
B. Task (no code)
- i. List the objects.
- ii. For each, list what it knows and what it does.
C. Rule hunt
Name at least one rule some object must enforce, and say which object.
D. Note
We revisit this exact problem in Week 2, so keep your answer.
11. Completion Criteria
A. Concepts
- i. I can explain HLD vs LLD to a friend in under a minute.
- ii. I can say what "thinking in objects" means.
B. Skills
- i. I can list objects, state, behavior and rules before writing code.
- ii. I can spot an
if/elsechain on a "type" and say what is changing.
C. Code
- i.
BankAccountandShoppingCartcannot be put in an invalid state from outside. - ii. Exercise 2 is done and
Carthas noifon customer type.
D. Reflection
I can explain why a pattern should wait until I feel the problem it solves.
12. Recall
A. Schedule
Revisit this lesson after 1 day, 3 days and 7 days.
B. What to do each time
Answer the three "Can You Explain?" questions without looking.
C. Where this resurfaces
- i. Lesson 4: Encapsulation.
- ii. Lesson 6: Responsibility.
- iii. Lesson 21: Open/Closed Principle.
- iv. Lesson 40: Strategy.
- v. Discount Engine and Parking Lot exercises.
D. Next
Day 2 — Classes vs objects: thinking in objects rather than data structures.
Notes & links
Saved automatically.