Skip to main content

βœ… Summary & Cheat Sheet

Congratulations! You've learned all 5 SOLID principles. Here's everything at a glance.


πŸ—ΊοΈ The Big Picture​

S β€” Single Responsibility β†’ One class, one job
O β€” Open/Closed β†’ Extend, don't modify
L β€” Liskov Substitution β†’ Subclasses keep parent's promise
I β€” Interface Segregation β†’ Small, focused interfaces
D β€” Dependency Inversion β†’ Depend on interfaces, not concretions

πŸ“‹ Principle Quick Reference​

S β€” Single Responsibility​

  • Rule: A class should have only one reason to change
  • Smell: The class description needs the word "AND"
  • Fix: Extract each responsibility into its own class
  • Spring example: Separate @Controller, @Service, @Repository
// ❌ Bad
class UserService { void save() {} void sendEmail() {} void log() {} }

// βœ… Good
class UserRepository { void save() {} }
class EmailService { void send() {} }
class AuditLogger { void log() {} }

O β€” Open/Closed​

  • Rule: Open for extension, closed for modification
  • Smell: Adding a feature requires adding another if/else to an existing class
  • Fix: Use interfaces and polymorphism; add new classes instead of changing old ones
  • Spring example: Strategy pattern with @Component beans in a Map
// ❌ Bad
if (type.equals("EMAIL")) { ... }
else if (type.equals("SMS")) { ... }

// βœ… Good
interface NotificationSender { void send(String msg); }
class EmailSender implements NotificationSender { ... }
class SmsSender implements NotificationSender { ... }

L β€” Liskov Substitution​

  • Rule: Subclasses must be drop-in replacements for their parent
  • Smell: A subclass throws UnsupportedOperationException or has an empty method body
  • Fix: Don't inherit just for code reuse β€” inherit for true "is-a" relationships; use interfaces instead
  • Spring example: All PaymentGateway implementations actually support charge()
// ❌ Bad: Square extends Rectangle breaks area calculation
// βœ… Good: Both implement Shape interface with getArea()

interface Shape { int getArea(); }
class Rectangle implements Shape { ... }
class Square implements Shape { ... }

I β€” Interface Segregation​

  • Rule: Don't force classes to implement methods they don't use
  • Smell: Empty method bodies or // not applicable comments in implementations
  • Fix: Break fat interfaces into small, focused ones
  • Spring example: Separate read/write repositories; separate sender interfaces
// ❌ Bad
interface Worker { void work(); void eat(); void sleep(); }

// βœ… Good
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Sleepable { void sleep(); }

D β€” Dependency Inversion​

  • Rule: Depend on abstractions (interfaces), not concrete classes
  • Smell: new ConcreteClass() inside a service or business logic class
  • Fix: Inject dependencies through constructor; depend on interfaces
  • Spring example: @Autowired with interface types; @Profile to swap implementations
// ❌ Bad
class OrderService {
private StripeGateway gateway = new StripeGateway(); // hardcoded!
}

// βœ… Good
class OrderService {
private final PaymentGateway gateway; // interface!
public OrderService(PaymentGateway gateway) { this.gateway = gateway; }
}

πŸ”— How They Work Together​

The SOLID principles aren't isolated rules β€” they complement each other:

PrincipleEnables
SRPSmaller, focused classes β†’ easier to apply OCP and DIP
OCPUses interfaces (ISP) and injection (DIP) to allow extension
LSPEnsures that OCP's substitutions actually work correctly
ISPMakes DIP easier β€” small interfaces are easier to implement and inject
DIPThe foundation for testability and flexibility in Spring

πŸ§ͺ Testing Is Your Signal​

If a class is hard to unit test, it's usually violating one or more SOLID principles:

SymptomLikely Violation
Can't test without a real databaseD β€” no interface for repository
Test class setup is 100 linesS β€” class does too much
Adding a new case breaks existing testsO β€” not open for extension
Mock throws unexpectedlyL β€” subclass doesn't honor contract
Mocking forces you to stub 10 unused methodsI β€” interface is too fat

🎯 Where to Go Next​

  • Practice: Refactor a class in your current project using these principles
  • Read: Clean Code by Robert C. Martin
  • Learn: Design patterns (Strategy, Factory, Observer) β€” they all build on SOLID
  • Explore: Spring's source code itself is a masterclass in SOLID applied to real frameworks

Interview Questions​

Q: How do SOLID principles interact when they appear to conflict?​

A: Treat them as balancing constraints, not absolute rules. For example, using OCP may introduce abstractions (DIP/ISP), but if abstraction cost is too high for a stable module, keep design simpler and refactor later.

Q: Give an example where strict OCP can hurt maintainability.​

A: If every small variation creates a new strategy class, teams may end up with class explosion and weak discoverability. For low-volatility logic, a simple branch can be more maintainable.

Q: How would you review a pull request for SOLID quality quickly?​

A: Check responsibility boundaries, dependency direction, contract safety, and interface size. Then verify if the change made testing easier or harder.

Q: What architecture smell shows both SRP and ISP violations at once?​

A: A service with many public methods serving unrelated use cases, plus consumers that only use small subsets of those methods.

Q: How does DIP support clean architecture layering?​

A: Domain and application layers depend on interfaces owned by higher-level policies, while infrastructure implements those interfaces. This keeps business rules independent of framework/vendor details.

Q: When is it acceptable to violate a SOLID principle temporarily?​

A: During short-lived exploration or incident fixes, if you capture technical debt and schedule cleanup. The violation should be explicit, bounded, and reversible.

Q: How do you measure whether SOLID adoption is working over time?​

A: Observe trend improvements in defect rate, test execution speed, change lead time, and blast radius of modifications.

Q: What is your senior-level rule for applying SOLID pragmatically?​

A: Optimize for changeability. Apply stronger abstractions where requirements evolve frequently, and keep straightforward code where volatility is low.