Showing posts with label Single Responsibility Principle. Show all posts
Showing posts with label Single Responsibility Principle. Show all posts

Sunday, September 21, 2025

Understanding SOLID Principles in Software Development with Real-Time Examples

 In modern software development, writing clean, maintainable, and scalable code is as important as delivering functionality. This is where SOLID principles play a crucial role. Introduced by Robert C. Martin (Uncle Bob), SOLID represents a set of five design principles that guide developers to create software that is easy to understand, modify, and extend without breaking existing functionality.

In this article, we’ll explore each SOLID principle with real-time examples in .NET Core, C#, and Angular applications.


1. Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change, meaning it should do only one job.

Example:

Suppose you have a class InvoiceManager handling both invoice creation and sending email notifications.

public class InvoiceManager { public void CreateInvoice() { // Logic to create invoice } public void SendEmailNotification() { // Logic to send email } }

👉 This violates SRP because the class handles two responsibilities.

Refactored:

public class InvoiceManager { public void CreateInvoice() { // Logic to create invoice } } public class EmailService { public void SendEmailNotification() { // Logic to send email } }

Now, InvoiceManager only deals with invoices, and EmailService manages emails.

Real-Time Example:
In an e-commerce application, the order processing service should only handle order logic, while a separate service should handle notifications.


2. Open/Closed Principle (OCP)

Definition: Software entities (classes, modules, functions) should be open for extension but closed for modification.

Example:

Imagine you have a PaymentProcessor class supporting Credit Card payments. Later, you need to add UPI payments.

❌ Bad approach – modifying the existing class:

public class PaymentProcessor { public void Process(string paymentType) { if(paymentType == "CreditCard") { // Process credit card } else if(paymentType == "UPI") { // Process UPI } } }

👉 This violates OCP because every new payment method requires changing the class.

✅ Better approach – use abstraction:

public interface IPayment { void Process(); } public class CreditCardPayment : IPayment { public void Process() => Console.WriteLine("Credit Card Payment Processed"); } public class UPIPayment : IPayment { public void Process() => Console.WriteLine("UPI Payment Processed"); } public class PaymentProcessor { public void ProcessPayment(IPayment payment) => payment.Process(); }

Now, you can add new payment methods without modifying the PaymentProcessor.

Real-Time Example:
A food delivery app can add new payment gateways (PayPal, Wallets, UPI, etc.) without modifying the existing payment processing logic.


3. Liskov Substitution Principle (LSP)

Definition: Objects of a superclass should be replaceable with objects of its subclasses without breaking the application.

Example:

Suppose you have a base class Bird with a method Fly().

public class Bird { public virtual void Fly() { } } public class Sparrow : Bird { public override void Fly() => Console.WriteLine("Sparrow flies"); } public class Ostrich : Bird { public override void Fly() => throw new NotImplementedException(); }

👉 Here, Ostrich violates LSP because an ostrich cannot fly. This breaks the substitution principle.

✅ Better approach:

public abstract class Bird { } public class FlyingBird : Bird { public void Fly() => Console.WriteLine("Flying bird"); } public class Ostrich : Bird { public void Run() => Console.WriteLine("Ostrich runs"); }

Now, the hierarchy respects LSP.

Real-Time Example:
In a transport booking system, a Car and a Bike can be substituted for a Vehicle, but a Flight may need a different interface since it doesn’t behave the same way on roads.


4. Interface Segregation Principle (ISP)

Definition: A client should not be forced to implement methods it does not use.

Example:

public interface IPrinter { void Print(); void Scan(); void Fax(); }

👉 If a basic printer only supports printing, it will still be forced to implement Scan() and Fax() unnecessarily.

✅ Better approach:

public interface IPrint { void Print(); } public interface IScan { void Scan(); } public interface IFax { void Fax(); } public class BasicPrinter : IPrint { public void Print() => Console.WriteLine("Printing..."); }

Real-Time Example:
In an employee management system, not all employees have payroll features. Instead of one large interface, split it into IWork, IManage, IPayroll so classes only implement what they need.


5. Dependency Inversion Principle (DIP)

Definition: High-level modules should not depend on low-level modules; both should depend on abstractions.

Example:

public class MySQLDatabase { public void SaveData(string data) => Console.WriteLine("Saved to MySQL"); } public class UserService { private MySQLDatabase _db = new MySQLDatabase(); public void SaveUser(string data) => _db.SaveData(data); }

👉 This tightly couples UserService with MySQLDatabase.

✅ Better approach:

public interface IDatabase { void SaveData(string data); } public class MySQLDatabase : IDatabase { public void SaveData(string data) => Console.WriteLine("Saved to MySQL"); } public class MongoDBDatabase : IDatabase { public void SaveData(string data) => Console.WriteLine("Saved to MongoDB"); } public class UserService { private readonly IDatabase _database; public UserService(IDatabase database) { _database = database; } public void SaveUser(string data) => _database.SaveData(data); }

Now, you can inject any database (MySQL, MongoDB, SQL Server) without modifying UserService.

Real-Time Example:
In a .NET Core Web API, you can use Dependency Injection (DI) to register services like ILogger, IRepository, or IDatabase so that switching implementations doesn’t require changing core business logic.


✅ Benefits of SOLID Principles

  • Improved code readability

  • Easy maintenance & testing

  • Encourages reusability & scalability

  • Reduces bugs caused by code changes

  • Supports clean architecture & design patterns

Blog Archive

Don't Copy

Protected by Copyscape Online Plagiarism Checker

Pages