Week 5 Lesson
SOLID Part 1: SRP, OCP, and LSP
The first three SOLID principles help you avoid classes that break when requirements change.
Learning Objectives
- Apply Single Responsibility Principle to split classes.
- Use Open/Closed Principle to add behavior safely.
- Recognize Liskov Substitution violations.
- Refactor a brittle inheritance design.
Lecture Notes
SOLID is a set of object-oriented design principles. The goal is not to memorize slogans. The goal is to notice design pressure before a system becomes painful. SRP, OCP, and LSP are especially useful for students because they expose common beginner designs: classes that do too much, switch statements that grow forever, and inheritance trees that look logical but behave incorrectly.
The Single Responsibility Principle says a class should have one main reason to change. This does not mean every class has only one method. It means the class has one coherent responsibility. A Report class might format report content. A ReportRepository might save reports. An EmailSender might deliver reports. If one class handles all three, a change in file storage can accidentally affect formatting.
The Open/Closed Principle says software should be open for extension but closed for repeated modification. Imagine a shipping calculator with a switch statement for Standard, Express, Overnight, International, Drone, and LocalCourier. Every new shipping type changes the same function. A better design defines a ShippingStrategy interface and adds new strategies without editing the main checkout policy each time.
The Liskov Substitution Principle says subclasses should be usable wherever the base type is expected. The classic Rectangle/Square example shows the danger. If Square extends Rectangle but rejects independent width and height changes, code that expects a Rectangle may break. The issue is not math; the issue is behavior. A child type must honor the expectations of the parent type.
SOLID is not a command to create endless interfaces. Applying SOLID well requires judgment. For a tiny script, a simple conditional may be fine. For a growing app with changing rules, extension points matter. The designer’s job is to protect the parts most likely to change without turning every line into an abstraction.
Worked Example: Open/Closed with shipping strategies
function shippingCost(type: string, weight: number) {
if (type === 'standard') return weight * 1.2;
if (type === 'express') return weight * 2.1;
if (type === 'overnight') return weight * 4.8;
throw new Error('Unknown shipping type');
}interface ShippingStrategy {
cost(weight: number): number;
}
class StandardShipping implements ShippingStrategy {
cost(weight: number) { return weight * 1.2; }
}
class ExpressShipping implements ShippingStrategy {
cost(weight: number) { return weight * 2.1; }
}
class Checkout {
constructor(private shipping: ShippingStrategy) {}
totalWithShipping(subtotal: number, weight: number) {
return subtotal + this.shipping.cost(weight);
}
}Lab: SOLID Refactor: Reports
Scenario: A ReportManager class loads data, calculates totals, formats HTML, saves files, and emails the report.
Steps
- List each reason ReportManager might change.
- Split the class into focused responsibilities.
- Identify one behavior that should be open for extension.
- Write or sketch the new interfaces/classes.
- Explain how the design improves testing.
Deliverables
- Refactored design
- Class list
- Short SOLID explanation
- Testability note
Assignment
Refactor a payment, shipping, grading, or enemy-behavior example to use SRP and OCP. Include before/after code.
Discussion Prompts
- Does SRP mean one method per class?
- When is OCP overkill?
- What makes a subclass unsafe?
Week 5 Quiz
Use these as quick checks after the lesson.
Question 1
SRP says a class should have:
Question 2
OCP encourages:
Question 3
LSP is mainly about:
Question 4
A growing switch statement over behavior may suggest:
Question 5
SOLID should be used:
Student Notes
Saved in this browser only.