SOLID Principles
Ā
1. Single Responsibility Principle (SRP)
Definition: A class should have only one reason to change, meaning it should do only one thing (one responsibility).
Problem: Classes that handle multiple responsibilities become large, hard to understand, and more likely to break when changes are made.
Solution: Split functionalities into smaller, focused classes ā each handling one specific concern.
Example: Instead of one InvoiceManager
handling invoice calculation, printing, and email sending, split it into InvoiceCalculator
, InvoicePrinter
, and InvoiceEmailer
.
2. Open/Closed Principle (OCP)
Definition: Software entities should be open for extension, but closed for modification.
Problem: Adding new functionality often requires modifying existing code, risking bugs in stable code.
Solution: Design code to be extended via inheritance, interfaces, or composition, without changing existing tested logic.
Example: A payment system that supports new payment methods (credit card, PayPal) by adding new classes without changing the core payment processing class.
3. Liskov Substitution Principle (LSP)
Definition: Objects of a superclass should be replaceable with objects of its subclass without breaking the application.
Problem: Subclasses sometimes override behavior in ways that violate expectations, causing runtime errors or incorrect results.
Solution: Ensure subclasses follow the contract defined by their parent classes ā no unexpected behavior changes.
Example: If Bird
has a method fly()
, then Penguin
(which cannot fly) should not inherit from Bird
in a way that breaks fly()
.
4. Interface Segregation Principle (ISP)
Definition: Clients should not be forced to depend on interfaces they do not use.
Problem: Large, "fat" interfaces force implementing classes to write empty or irrelevant methods.
Solution: Break large interfaces into smaller, more specific ones.
Example: Instead of one IMachine
interface with print()
, scan()
, and fax()
, create separate IPrinter
, IScanner
, and IFax
interfaces.
5. Dependency Inversion Principle (DIP)
Definition: High-level modules should not depend on low-level modules ā both should depend on abstractions.
Problem: Code tightly coupled to concrete implementations is hard to change or test.
Solution: Depend on interfaces or abstract classes; use dependency injection to provide implementations.
Example: A ReportGenerator
should depend on an IReportFormatter
interface, not directly on a PDFReportFormatter
class.
š” In summary:
- S ā One class, one job.
- O ā Extend without changing existing code.
- L ā Subclasses must honor parent behavior.
- I ā Keep interfaces focused.
- D ā Depend on abstractions, not concrete classes.