Understanding SOLID Principles: A Beginner-Friendly Guide

Lakmuthu Dodamwalage
4 min readMar 2, 2025

--

When developing software, writing clean and maintainable code is crucial. The SOLID principles are five design principles that help in building scalable and efficient applications. These principles make the code easier to understand, modify, and extend without introducing unexpected issues.

Why Are SOLID Principles Important?

Following the SOLID principles helps in:

  • Improving code maintainability.
  • Making it easier to add new features.
  • Enhancing testability and scalability.
  • Reducing dependencies between components, making the code more flexible.

The SOLID acronym stands for:

  • S — Single Responsibility Principle (SRP)
  • O — Open/Closed Principle (OCP)
  • L — Liskov Substitution Principle (LSP)
  • I — Interface Segregation Principle (ISP)
  • D — Dependency Inversion Principle (DIP)

The first three principles primarily apply to classes, while the last two affect interfaces and dependencies. Let’s explore each principle in detail.

1. Single Responsibility Principle (SRP)

“A class should have only one reason to change.”

This principle states that a class should have only one responsibility. If a class is handling multiple concerns, it becomes difficult to maintain and modify.

Example

Violating SRP (Mixing user and payment logic in the same class)

class User {
public function getUserDetails($id) {
// Fetch user details
}

public function processPayment($amount) {
// Payment logic (should be in a separate class)
}
}

Following SRP (Separate concerns into different classes)

class User {
public function getUserDetails($id) {
// Fetch user details
}
}

class Payment {
public function processPayment($amount) {
// Handle payment
}
}

2. Open/Closed Principle (OCP)

“Software entities should be open for extension but closed for modification.”

This means we should be able to add new functionality without modifying existing code.

Example

Violating OCP (Modifying the existing class for a new payment method)

class Payment {
public function processPayment($type) {
if ($type == 'credit_card') {
// Process credit card payment
} elseif ($type == 'paypal') {
// Process PayPal payment (modifying existing code)
}
}
}

Following OCP (Using polymorphism to add new payment methods without modifying existing code)

interface PaymentMethod {
public function pay();
}

class CreditCardPayment implements PaymentMethod {
public function pay() {
// Process credit card payment
}
}

class PayPalPayment implements PaymentMethod {
public function pay() {
// Process PayPal payment
}
}

Now, adding a new payment method (e.g., BitcoinPayment) does not require modifying the PaymentMethod interface.

3. Liskov Substitution Principle (LSP)

“Derived classes must be substitutable for their base classes.”

This principle ensures that child classes can replace parent classes without breaking the application.

Example

Violating LSP (Child class changes the expected behavior)

class Rectangle {
protected $width;
protected $height;

public function setWidth($width) {
$this->width = $width;
}

public function setHeight($height) {
$this->height = $height;
}
}

class Square extends Rectangle {
public function setWidth($width) {
$this->width = $width;
$this->height = $width; // Unexpected behavior!
}
}

Following LSP (Avoiding unexpected behavior by keeping separate classes)

class Rectangle {
protected $width;
protected $height;
public function setWidth($width) { $this->width = $width; }
public function setHeight($height) { $this->height = $height; }
}

class Square {
protected $side;
public function setSide($side) { $this->side = $side; }
}

4. Interface Segregation Principle (ISP)

“Clients should not be forced to depend on interfaces they do not use.”

Instead of creating large interfaces, break them into smaller, specific interfaces that are relevant to the implementing class.

Example

Violating ISP (Forcing classes to implement irrelevant methods)

interface Worker {
public function work();
public function eat();
}

class Developer implements Worker {
public function work() {
// Coding logic
}
public function eat() {
// Not relevant for a developer class
}
}

Following ISP (Creating multiple interfaces for different concerns)

interface Workable {
public function work();
}

interface Eatable {
public function eat();
}

class Developer implements Workable {
public function work() {
// Coding logic
}
}

5. Dependency Inversion Principle (DIP)

“High-level modules should not depend on low-level modules. Both should depend on abstractions.”

Instead of hardcoding dependencies, use dependency injection to make the code more flexible and testable.

Example

Following DIP (Using abstraction to handle payments)

interface PaymentMethod {
public function pay();
}

class StripePayment implements PaymentMethod {
public function pay() {
// Stripe payment logic
}
}

class PayPalPayment implements PaymentMethod {
public function pay() {
// PayPal payment logic
}
}

class ProcessPayment {
private $paymentMethod;

public function __construct(PaymentMethod $paymentMethod) {
$this->paymentMethod = $paymentMethod;
}

public function process() {
$this->paymentMethod->pay();
}
}

Now, the ProcessPayment class can work with any payment method without modifying its implementation.

When Should You Follow SOLID Principles?

While SOLID principles are useful, they are not mandatory in every situation. Here’s when to apply them:

Use SOLID if:

  • You’re working on a large application.
  • You need scalability and flexibility.
  • Your project requires long-term maintenance.
  • You want better testability.

Be flexible if:

  • You’re building a small prototype.
  • Applying SOLID makes the code overcomplicated.
  • Performance is critical and excessive abstraction causes overhead.

Final Thoughts

SOLID is not a hard rule, but a guideline to write better software. The key is to balance simplicity and maintainability while following these principles effectively.

--

--

Lakmuthu Dodamwalage
Lakmuthu Dodamwalage

Written by Lakmuthu Dodamwalage

Software Engineer with 3+ years of experience in Laravel, PHP, AWS, and Docker. Passionate about back-end development, cloud tech.

No responses yet