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
+ 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
Customer OrderSvc PaymentSvc EmailSvc placeOrder(cart) validatePayment(card) paymentConfirmed sendEmail(email) sent orderId synchronous call return / async alt [condition] = conditional block
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
+ 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
TASK 01
SOLID Violation Hunt — 4 Snippets
~2 hrs · identify + fix

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
~1.5 hrs · diagram

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
~1 hr · diagram

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
~4 hrs · code + diagram

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.