TRACK A · LLD · MODULE A1 · WEEK 3
SOLID
Principles + OOP + UML
Prereq Phase 0
Unlocks All 23 Design Patterns
Task Parking Lot Refactoring
Duration 1 Week
Track A — Low-Level Design
Encapsulation
bundle + hide + expose only what's needed
Bundle data and behaviour. Hide internal state behind methods. Expose only a stable public interface. When internals change, callers don't break.
Abstraction
expose WHAT, hide HOW
Program to interfaces, not implementations. Callers know what a thing does, not how. Foundation of Strategy, Factory, Bridge patterns.
Inheritance
IS-A relationships only — prefer composition
Child inherits parent's state + behaviour. Valid only for true IS-A relationships (Dog IS-A Animal). For HAS-A use composition. Inheritance = tight coupling.
Polymorphism
same interface, different behaviour
One method call, many implementations. Runtime polymorphism via overriding. Compile-time via overloading. Core to OCP and all design patterns that use substitution.
⚠️ CRITICAL RULE
Favour Composition Over Inheritance
Inheritance creates tight coupling — when the parent changes, all children may break. Use inheritance only when the IS-A relationship is semantically true and stable. Use composition (HAS-A) when behaviour needs to be flexible or swappable at runtime. Most design patterns exploit composition, not inheritance.
✗ Inheritance: Duck extends Bird (breaks for robots)
✓ Composition: Duck has FlyBehaviour (swappable)
S — Single Responsibility
O — Open/Closed
L — Liskov Substitution
I — Interface Segregation
D — Dependency Inversion
S
Single Responsibility Principle
SRP · Robert C. Martin
"A class should have only one reason to change."
✗ VIOLATION — 4 reasons to change
class Invoice {
// Reason 1: business logic changes
double calculateTotal() { ... }
// Reason 2: DB schema changes
void saveToDatabase() { ... }
// Reason 3: report format changes
void printInvoice() { ... }
// Reason 4: email provider changes
void sendEmail() { ... }
}
✓ FIX — one responsibility each
class Invoice {
// Only: business logic
double calculateTotal() { ... }
}
class InvoiceRepository {
// Only: persistence
void save(Invoice inv) { ... }
}
class InvoicePrinter {
// Only: printing
void print(Invoice inv) { ... }
}
class InvoiceEmailer {
// Only: email
void send(Invoice inv) { ... }
}
SRP violations are the #1 reason codebases become unmaintainable. In interviews, when asked "what's wrong with this design?", look for classes doing too many things. Count the "reasons to change" — should always be 1.
O
Open/Closed Principle
OCP · Bertrand Meyer, popularised by Martin
"Open for extension, closed for modification."
✗ VIOLATION — modify for every new type
class DiscountCalc {
double calculate(String type, double p) {
if (type.equals("REGULAR")) return p * 0.95;
if (type.equals("PREMIUM")) return p * 0.85;
if (type.equals("EMPLOYEE")) return p * 0.70;
// Every new type = modify this class!
return p;
}
}
✓ FIX — extend without modifying
interface DiscountStrategy {
double apply(double price);
}
class RegularDiscount implements DiscountStrategy {
public double apply(double p) { return p*0.95; }
}
// New type = new class, touch NOTHING else:
class VIPDiscount implements DiscountStrategy {
public double apply(double p) { return p*0.60; }
}
OCP is directly embodied by the Strategy Pattern (A2) and Template Method (A4). When you see an if/else chain branching on type — that's OCP violation territory. The fix is almost always a Strategy or polymorphism.
L
Liskov Substitution Principle
LSP · Barbara Liskov, 1987
"Subtypes must be substitutable for their base types without altering program correctness."
✗ VIOLATION — Square breaks Rectangle contract
class Rectangle {
void setWidth(int w) { this.width = w; }
void setHeight(int h) { this.height = h; }
}
class Square extends Rectangle {
void setWidth(int w) { width=w; height=w; }
void setHeight(int h) { width=h; height=h; }
}
// testArea(r): r.setWidth(5); r.setHeight(4);
// Passes for Rectangle, FAILS for Square!
✓ FIX — use abstraction instead
interface Shape {
int area();
}
class Rectangle implements Shape {
int area() { return width * height; }
}
class Square implements Shape {
int area() { return side * side; }
}
// Both honour the Shape contract ✓
LSP violations show up as
instanceof checks or UnsupportedOperationException throws. These scream "inheritance hierarchy is wrong." Common trap: Penguin extends Bird (fly() throws!).I
Interface Segregation Principle
ISP · Robert C. Martin
"Clients should not be forced to depend on interfaces they do not use."
✗ VIOLATION — fat interface forces stubs
interface Worker {
void work();
void eat(); // robots don't eat
void sleep(); // robots don't sleep
}
class RobotWorker implements Worker {
void work() { ... }
void eat() { throw new UnsupportedOperationException(); }
void sleep() { throw new UnsupportedOperationException(); }
}
✓ FIX — focused role interfaces
interface Workable { void work(); }
interface Feedable { void eat(); }
interface Restable { void sleep(); }
class HumanWorker implements
Workable, Feedable, Restable { ... }
class RobotWorker implements
Workable { ... }
// Only what it needs — no stubs!
ISP violations are common in legacy codebases. Look for: interfaces with 10+ methods, classes implementing interface methods that throw UnsupportedOperationException, or clients importing an interface but only using 2 of its 12 methods.
D
Dependency Inversion Principle
DIP · Robert C. Martin
"High-level modules should not depend on low-level modules. Both should depend on abstractions."
✗ VIOLATION — hard-coded concrete deps
class OrderService {
private MySQLDatabase database;
private EmailService emailer;
public OrderService() {
this.database = new MySQLDatabase(); // ✗
this.emailer = new EmailService(); // ✗
}
}
// To switch to PostgreSQL — must change OrderService!
✓ FIX — inject abstractions
interface OrderRepository { void save(Order o); }
interface NotificationService { void notify(Order o); }
class OrderService {
private final OrderRepository repo;
private final NotificationService notif;
public OrderService(OrderRepository r,
NotificationService n) {
this.repo = r; this.notif = n;
}
}
// Wiring: new OrderService(new PostgreSQL(), new SMS());
DIP is the principle that makes unit testing possible. Without it, you can't mock dependencies. In LLD interviews, always inject dependencies via constructor — never instantiate them inside the class.
🔍 Quick-Reference: SOLID Violation Scanner
S
Class with 5+ unrelated methodsSplit into focused classes with one responsibility each. Count "reasons to change."
O
if/else or switch on type/stringReplace with polymorphism. New types = new classes. Strategy Pattern is the canonical fix.
L
Subclass throws UnsupportedOperationExceptionRedesign inheritance hierarchy. Use abstraction or composition instead.
L
instanceof checks in polymorphic codeFix the inheritance hierarchy. Use proper polymorphism — callers should never check the type.
I
Fat interface with 10+ methodsSplit into role-specific interfaces. Each client depends only on what it uses.
D
new ConcreteClass() inside service/classIntroduce an interface. Inject the dependency via constructor (or DI framework).
D
Hard to unit test — can't mock dependenciesClassic DIP violation. Introduce interface, inject dependency, mock in tests.
📋 Interview One-Liners — Say These Verbatim
S
— "Each class has one reason to change" → enables Strategy, Template Method
O
— "Open for extension, closed for modification" → Strategy, Template Method
L
— "Subtypes must honour the parent's contract" → prevents bad inheritance
I
— "Many focused interfaces > one fat interface" → enables clean composition
D
— "Depend on abstractions, not concretions" → Factory, DI containers
CLASS DIAGRAM — Relationship Symbols
A ────── B
Association
A uses/knows B
A ◇──── B
Aggregation
HAS-A (weak): B can exist without A
A ◆──── B
Composition
HAS-A (strong): B cannot exist without A
A ────▷ B
Inheritance
IS-A: A extends B (solid line)
A - - -▷ B
Implementation
A implements interface B (dashed)
A - - -> B
Dependency
A depends on B transitively
CLASS BOX NOTATION
ClassName
-privateField: Type
#protectedField: Type
+publicField: Type
+ publicMethod(): Type
- privateMethod(): void
AbstractClass
#state: int
+ concreteMethod(): void
+ abstractMethod(): void
<<interface>>
Printable
Printable
+ print(): void
+ scan(): void
- = private
# = protected
+ = public
Multiplicities:
1 exactly one
0..1 zero or one
* zero or more
1..* one or more
SEQUENCE DIAGRAM NOTATION
REFACTORED PARKING LOT — UML Class Diagram (SOLID-compliant)
ParkingLot
-name: String
-capacity: int
+ park(vehicle): Ticket
+ unpark(ticket): Bill
1..*
ParkingFloor
-floorNum: int
+ getAvailableSpot(): ParkingSpot
1..*
ParkingSpot
-spotNum: int
-occupied: bool
+ canFit(v): bool
+ isAvailable(): bool
◁
CarSpot
+canFit(v): bool
BikeSpot
+canFit(v): bool
TruckSpot
+canFit(v): bool
<<interface>>
PricingStrategy
PricingStrategy
+ calculateFee(h): double
HourlyPricing
+ calculateFee(h): double
FlatRatePricing
+ calculateFee(h): double
SRP: ParkingLot, Floor, Spot, Ticket, Bill, PricingStrategy all separate
OCP: New vehicle type = new Spot subclass, zero modifications to existing
DIP: PricingStrategy injected — swap hourly/flat without changing Spot
LSP: All Spot subtypes honour canFit() contract — no UnsupportedOperation
OCP: New vehicle type = new Spot subclass, zero modifications to existing
DIP: PricingStrategy injected — swap hourly/flat without changing Spot
LSP: All Spot subtypes honour canFit() contract — no UnsupportedOperation
TASK 01
SOLID Violation Hunt — 4 Snippets
›
For each snippet: name the principle violated, explain why, and write the fixed version.
// Snippet A — which principle?
class Report {
String generateHTML() { ... }
String generatePDF() { ... }
void saveToFile(String path) { ... }
void uploadToS3() { ... }
void emailReport(String to) { ... }
}
// Snippet B — which principle?
class Square extends Rectangle {
void setWidth(int w) { this.width=w; this.height=w; }
void setHeight(int h) { this.width=h; this.height=h; }
}
// Snippet C — which principle?
interface Printable {
void print(); void scan(); void fax(); void copy();
}
class BasicPrinter implements Printable {
void print() { ... }
void scan() { throw new UnsupportedOperationException(); }
void fax() { throw new UnsupportedOperationException(); }
void copy() { throw new UnsupportedOperationException(); }
}
// Snippet D — which principle?
class NotificationService {
void notify(String type, String message) {
if (type.equals("EMAIL")) sendEmail(message);
else if (type.equals("SMS")) sendSMS(message);
else if (type.equals("PUSH")) sendPush(message);
}
}
TASK 02
UML Class Diagram — Library Management System
›
Draw the UML class diagram with these entities. Include all attributes, methods, relationships with correct symbols, and multiplicities.
Entities: Library, Member, Book, BookCopy, Loan, Librarian
Requirements:
- A Library has many Members and many BookCopies
- A Book can have multiple BookCopies
- A Loan connects a Member to a BookCopy (with dates)
- A Librarian manages loans
- Members have a borrowing limit (max 3 books)
- BookCopy has status: AVAILABLE, BORROWED, RESERVED
Include: composition vs aggregation distinction
inheritance if Librarian IS-A Member
all multiplicities on every relationship
TASK 03
Sequence Diagram — E-Commerce Order Flow
›
Draw a sequence diagram for the complete order placement flow including the failure path.
Actors: Customer, OrderService, PaymentService,
InventoryService, EmailService
Happy path:
1. Customer → OrderService: placeOrder(cart)
2. OrderService → PaymentService: validatePayment(card)
3. PaymentService → OrderService: paymentConfirmed
4. OrderService → InventoryService: reserveItems(cart)
5. InventoryService → OrderService: reservationId
6. OrderService → EmailService: sendConfirmation(email)
7. OrderService → Customer: orderId
Also model: what happens when payment fails?
Show the failure path as an alt block
⭐ PROJECT
Parking Lot Refactoring — Full LLD Exercise
›
The violating code below has multiple SOLID violations. List every violation with line references, produce refactored Java code, and draw the UML class diagram of your solution.
class ParkingLot {
private int totalSpots = 100;
private int occupiedSpots = 0;
private List<String[]> parkedVehicles = new ArrayList<>();
public String parkVehicle(String vehicleType, String plate) {
if (occupiedSpots >= totalSpots) return "Lot is full";
// ← VIOLATION: what principle? why?
double rate;
if (vehicleType.equals("CAR")) rate = 20.0;
else if (vehicleType.equals("BIKE")) rate = 10.0;
else if (vehicleType.equals("TRUCK")) rate = 40.0;
else return "Unknown vehicle type";
// ← VIOLATION: what principle? why?
String ticketId = "T" + System.currentTimeMillis();
parkedVehicles.add(new String[]{ticketId, plate,
vehicleType, String.valueOf(System.currentTimeMillis()),
String.valueOf(rate)});
occupiedSpots++;
// ← VIOLATION: what principle? why?
System.out.println("Parked " + vehicleType);
// ← VIOLATION: what principle? why?
saveToDatabase(ticketId, plate);
return ticketId;
}
private void saveToDatabase(String id, String plate) {
System.out.println("Saving to MySQL: " + id); // hardcoded!
}
public double calculateBill(String ticketId) {
// billing logic crammed here ← VIOLATION: what principle?
for (String[] v : parkedVehicles) {
if (v[0].equals(ticketId)) {
long entry = Long.parseLong(v[3]);
double hours = (System.currentTimeMillis()-entry)/3600000.0;
double rate = Double.parseDouble(v[4]);
return Math.ceil(hours) * rate;
}
}
return 0;
}
}
Deliverable:
1. List each violation (principle + explanation)
2. Refactored Java code (multiple classes)
3. UML class diagram of the refactored design
0 / 9 completed
A1 → SOLID + OOP + UML
Can explain all 5 SOLID principles from memory without notes
Can identify SOLID violations in code and name the exact principle
Know all 4 OOP pillars and can give a concrete example of each
Can draw UML class diagrams with correct notation: ◆ ◇ ▷ dashed
Understand composition vs aggregation vs association distinction
Can draw sequence diagrams with lifelines, messages, and alt blocks
✏️ Task 1: All 4 SOLID violation snippets fixed with principle named
✏️ Task 2 + 3: Library UML + E-Commerce sequence diagram drawn
✏️ Mini Project: Parking Lot violations listed + refactored code + UML
NEXT MODULE
A2 — Creational Design Patterns
Singleton, Factory, Abstract Factory, Builder, Prototype — each mapped to a real system. Mini Project: ATM System design using Creational patterns.