Skip to main content
Design Patterns – Complete Beginner to Advanced Guide
CHAPTER 03 Intermediate

SOLID Principles

Updated: May 16, 2026
30 min read

# CHAPTER 3

SOLID Principles

1. Introduction

You can know everything about OOP—classes, interfaces, and polymorphism—and still write terrible, unmaintainable code. Creating 5,000-line "God Classes" that handle databases, emails, and UI rendering simultaneously is technically valid OOP, but it is an architectural nightmare. To write clean, enterprise-grade code, developers rely on the SOLID Principles. Introduced by Robert C. Martin (Uncle Bob), these five design principles are the absolute standard for creating software that is easy to maintain, understand, and extend. Every Design Pattern you will learn is simply an application of these SOLID rules. In this chapter, we will master the five principles and learn how to refactor messy code into elegant architecture.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Define all five SOLID principles (SRP, OCP, LSP, ISP, DIP).
  • Refactor a "God Class" using the Single Responsibility Principle.
  • Design extensible systems using the Open/Closed Principle.
  • Understand the danger of violating the Liskov Substitution Principle.
  • Apply Dependency Inversion to decouple high-level logic from low-level modules.

3. S - Single Responsibility Principle (SRP)

"A class should have one, and only one, reason to change."
  • The Anti-Pattern: A User class that holds the user's name, connects to the MySQL database to save the user, and sends a "Welcome" email. This class has three responsibilities. If the database changes, or the email API changes, this class must be rewritten.
  • The Solution: Split it up. Create a User class (holds data), a UserRepository class (handles the database), and a UserMailer class (handles emails). Now, each class has only one job.

4. O - Open/Closed Principle (OCP)

"Software entities (classes, modules, functions) should be open for extension, but closed for modification."
  • The Anti-Pattern: A ShippingCalculator class with a massive if/else block: if (type == 'ground') return 5; else if (type == 'air') return 20;. Every time you add a new shipping method, you have to modify this existing, tested class, risking breaking old code.
  • The Solution: Create a ShippingStrategy interface. Create separate classes GroundShipping and AirShipping that implement the interface. The Calculator just accepts the interface. To add DroneShipping, you write a *new* class. You *extended* the system without *modifying* the Calculator class.

5. L - Liskov Substitution Principle (LSP)

"Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program."
  • The Concept: If Class B extends Class A, you should be able to pass Class B into any function that expects Class A, and the program should not crash or behave weirdly.
  • The Anti-Pattern: A Bird class has a fly() method. A Penguin class extends Bird. But penguins can't fly, so the Penguin->fly() method throws an Exception. You have violated LSP, because a Penguin cannot truly substitute a Bird.
  • The Solution: Refactor the hierarchy. Create a FlyingBird interface and only have Eagles and Sparrows implement it.

6. I - Interface Segregation Principle (ISP)

"Many client-specific interfaces are better than one general-purpose interface."
  • The Anti-Pattern: A massive Worker interface with work(), eat(), and sleep() methods. A HumanWorker implements it perfectly. A RobotWorker implements it, but is forced to write empty, useless eat() and sleep() methods because robots don't eat.
  • The Solution: Break the fat interface into smaller ones: Workable, Eatable, Sleepable. The Human implements all three. The Robot only implements Workable.

7. D - Dependency Inversion Principle (DIP)

"Depend upon abstractions, not concretions."
  • The Anti-Pattern: A high-level OrderProcessor class directly instantiates a low-level MySQLDatabase class inside its constructor using the new keyword. The OrderProcessor is now permanently glued to MySQL.
  • The Solution: The OrderProcessor should accept a DatabaseInterface through its constructor (Dependency Injection). You pass the MySQL class *into* the processor from the outside. Later, you can easily pass in a MongoDBDatabase class instead, and the OrderProcessor won't even notice.

8. Code Examples: Refactoring SRP

*Before (Violating SRP):*
php
12345
class Invoice {
    public function calculateTotal() { /* Math */ }
    public function printInvoice() { /* HTML Rendering */ }
    public function saveToDatabase() { /* SQL Query */ }
}

*After (Applying SRP):*

php
123456789
class Invoice {
    public function calculateTotal() { /* Math */ }
}
class InvoicePrinter {
    public function print(Invoice $invoice) { /* HTML Rendering */ }
}
class InvoiceRepository {
    public function save(Invoice $invoice) { /* SQL Query */ }
}

9. Best Practices

  • Pragmatism over Dogma: Writing perfectly SOLID code often means creating 5 files instead of 1. For a massive enterprise app, this modularity is mandatory. For a tiny, 100-line script, strictly adhering to SOLID might be massive overkill. Apply the principles where complexity demands it.

10. Mini Project: SOLID Refactoring

  1. 1. The Scenario: You inherit a ReportGenerator class. It fetches data from an API, formats it as JSON, and writes it to a local file. It violates SRP, OCP, and DIP.
  1. 2. The Refactor:
  • Create a DataFetcher interface (DIP).
  • Create a Formatter interface with a format() method. Implement JsonFormatter and XmlFormatter (OCP).
  • Create a FileWriter class (SRP).
  • Inject these dependencies into the ReportGenerator constructor.

11. Practice Exercises

  1. 1. Define the Single Responsibility Principle (SRP). Why is a class that handles both business logic and database queries a violation of this principle?
  1. 2. Explain the Dependency Inversion Principle (DIP). How does "injecting" dependencies via the constructor make a class significantly easier to Unit Test?

12. MCQs with Answers

Question 1

A developer needs to add a new "Bitcoin" payment method to an application. Instead of editing the existing PaymentProcessor class to add an if (type == 'bitcoin') statement, they create a new BitcoinPayment class that implements a PaymentInterface. Which SOLID principle did they successfully apply?

Question 2

Which principle states that a high-level module (like a Checkout system) should not directly instantiate and depend on a low-level concrete class (like a Stripe API library), but should instead depend on an abstraction (like a PaymentGateway Interface)?

13. Interview Questions

  • Q: Explain the Liskov Substitution Principle using the classic "Square/Rectangle" problem. Why does making a Square inherit from a Rectangle often violate LSP?
  • Q: What is the Interface Segregation Principle? Walk me through a scenario where forcing a class to implement a "fat" interface leads to bad architectural design.
  • Q: How do the SOLID principles directly relate to the Gang of Four Design Patterns? (Hint: Patterns are just standardized implementations of these principles).

14. FAQs

Q: Who created the SOLID principles? A: The principles were promoted by Robert C. Martin (also known as Uncle Bob) in the early 2000s in his writings on Agile Software Development and Clean Architecture.

15. Summary

In Chapter 3, we transitioned from writing code that simply "works" to writing code that can survive years of enterprise development. We mastered the SOLID principles. We learned to break apart God Classes into focused modules (SRP), extend systems without breaking existing code (OCP), ensure our inheritance hierarchies are logically sound (LSP), keep our interfaces thin and focused (ISP), and decouple our high-level architecture from low-level details using dependency injection (DIP). These five rules are the compass by which all great software architects navigate complexity.

16. Next Chapter Recommendation

Before we start writing specific Design Patterns, we need a visual way to plan them on a whiteboard. Proceed to Chapter 4: UML and Software Modeling.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·