1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: SOLID in practice = refactor toward principles.
- SRP refactoring: Extract responsibilities into separate classes.
- OCP refactoring: Replace if-else with polymorphism (Strategy pattern).
- LSP refactoring: Fix substitutability violations (Square ≠ Rectangle).
- ISP refactoring: Split fat interfaces into focused ones.
- DIP refactoring: Inject dependencies via interfaces.
- Process: Identify smell → apply principle → verify improvement.
- Key: Refactor incrementally with tests!
Think of home renovation. Refactoring = remodel step-by-step (not demolish). SRP = one room, one purpose (bedroom not kitchen+office). OCP = modular furniture (add pieces without walls). DIP = universal outlets (plug in anything). Tests = safety inspector (verify each change)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- SRP Refactoring: Swiss Army knife → separate tools
- OCP Refactoring: Hard-coded menu → plug-in system
- DIP Refactoring: Direct wiring → power outlets
3. Technical Mastery (The "Deep Dive")
java
// ===========================================
// 1. SRP REFACTORING
// ===========================================
// ❌ BEFORE: God class violating SRP
class UserService {
void register(User user) {
// Validation
if (user.getEmail() == null) throw new Exception();
// Save to database
Connection conn = getConnection();
// SQL...
// Send email
EmailClient.send(user.getEmail(), "Welcome");
// Log
System.out.println("User registered");
}
}
// Problem: 4 responsibilities (validation, DB, email, logging)
// ✅ AFTER: SRP applied
class UserService {
private final UserValidator validator;
private final UserRepository repository;
private final EmailService emailService;
private final AuditLogger logger;
void register(User user) {
validator.validate(user);
repository.save(user);
emailService.sendWelcome(user);
logger.log("User registered: " + user.getId());
}
}
class UserValidator {
void validate(User user) {
if (user.getEmail() == null) {
throw new IllegalArgumentException("Email required");
}
}
}
// ===========================================
// 2. OCP REFACTORING
// ===========================================
// ❌ BEFORE: Violates OCP (modify for new types)
class DiscountCalculator {
double calculate(String customerType, double amount) {
if ("PREMIUM".equals(customerType)) {
return amount * 0.8; // 20% discount
} else if ("GOLD".equals(customerType)) {
return amount * 0.9; // 10% discount
} else if ("SILVER".equals(customerType)) {
return amount * 0.95; // 5% discount
}
return amount;
}
}
// Add new type? Modify this method!
// ✅ AFTER: OCP applied (Strategy pattern)
interface DiscountStrategy {
double apply(double amount);
}
class PremiumDiscount implements DiscountStrategy {
public double apply(double amount) {
return amount * 0.8;
}
}
class GoldDiscount implements DiscountStrategy {
public double apply(double amount) {
return amount * 0.9;
}
}
class DiscountCalculator {
double calculate(DiscountStrategy strategy, double amount) {
return strategy.apply(amount);
}
}
// Add new discount? Create new class, no modification!
// ===========================================
// 3. LSP REFACTORING
// ===========================================
// ❌ BEFORE: LSP violation
class Rectangle {
protected int width, height;
void setWidth(int width) { this.width = width; }
void setHeight(int height) { this.height = height; }
int area() { return width * height; }
}
class Square extends Rectangle {
@Override
void setWidth(int width) {
this.width = width;
this.height = width; // ❌ Breaks LSP!
}
}
// Test:
void testRectangle(Rectangle r) {
r.setWidth(5);
r.setHeight(10);
assert r.area() == 50; // ❌ Fails for Square!
}
// ✅ AFTER: LSP applied (composition)
interface Shape {
int area();
}
class Rectangle implements Shape {
private final int width, height;
Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int area() {
return width * height;
}
}
class Square implements Shape {
private final int side;
Square(int side) {
this.side = side;
}
public int area() {
return side * side;
}
}
// ===========================================
// 4. ISP REFACTORING
// ===========================================
// ❌ BEFORE: Fat interface
interface Worker {
void work();
void eat();
void sleep();
}
class Robot implements Worker {
public void work() { /* work */ }
public void eat() { /* ❌ Robots don't eat! */ }
public void sleep() { /* ❌ Robots don't sleep! */ }
}
// ✅ AFTER: ISP applied (segregated interfaces)
interface Workable {
void work();
}
interface Eatable {
void eat();
}
interface Sleepable {
void sleep();
}
class Human implements Workable, Eatable, Sleepable {
public void work() { /* work */ }
public void eat() { /* eat */ }
public void sleep() { /* sleep */ }
}
class Robot implements Workable {
public void work() { /* work */ }
// No forced methods!
}
// ===========================================
// 5. DIP REFACTORING
// ===========================================
// ❌ BEFORE: Tight coupling (depends on concrete class)
class OrderProcessor {
private MySQLDatabase database = new MySQLDatabase(); // ❌ Concrete!
void process(Order order) {
database.save(order);
}
}
// Can't switch to PostgreSQL without modifying OrderProcessor!
// ✅ AFTER: DIP applied (depend on abstraction)
interface Database {
void save(Order order);
}
class MySQLDatabase implements Database {
public void save(Order order) {
// MySQL-specific code
}
}
class PostgreSQLDatabase implements Database {
public void save(Order order) {
// PostgreSQL-specific code
}
}
class OrderProcessor {
private final Database database; // ✅ Abstraction!
OrderProcessor(Database database) { // Dependency injection
this.database = database;
}
void process(Order order) {
database.save(order);
}
}
// Usage: Easy to switch!
Database db = new PostgreSQLDatabase();
OrderProcessor processor = new OrderProcessor(db);5. The Comparison & Decision Layer
| Principle | Smell | Refactoring |
|---|---|---|
| SRP | God class | Extract classes by responsibility |
| OCP | if-else chains | Strategy/Factory pattern |
| LSP | Broken substitution | Use composition over inheritance |
| ISP | Forced methods | Split interfaces |
| DIP | new ConcreteClass() | Inject via interface |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "How do you refactor toward SOLID?" Answer: Incremental refactoring with tests!
Process:
- Write tests for existing behavior
- Identify violation (God class, if-else chain, etc.)
- Apply principle (extract class, introduce interface, etc.)
- Run tests to verify no breakage
- Repeat for next violation
java
// Example: SRP refactoring
// 1. Test existing UserService
@Test
void testRegister() {
UserService service = new UserService();
service.register(new User("test@example.com"));
// Verify behavior
}
// 2. Extract EmailService (one responsibility at a time)
// 3. Run tests (still pass!)
// 4. Extract UserValidator
// 5. Run tests (still pass!)
// ...Pro-Tip: SOLID Refactoring Workflow:
text
1. RED: Write failing test
2. GREEN: Make it pass (ugly code OK!)
3. REFACTOR: Apply SOLID principles
4. Test still GREEN → safe refactoring!
Never refactor without tests!