SOLID Principles:
Hello readers, I am trying to explain SOLID principles to you in an easy way. After reading this article, if you have any questions, please reach out to me.
In the programming world, these SOLID principles are very important; we will write the code by implementing these principles.
- S: Single Repository Pattern(SRP).
- O: Open and Closed Principle(OCP).
- L: Liskov Substitution Principle(LSP).
- I: Interface Segregation Principle(ISP).
- D: Dependency Inversion Principle(DIP).
Single Repository Pattern (SRP)
In its full form, "Single" means "only Once". This principle tells us that a class should have only one reason to change or should focus on performing one task.
using System;
class SRPExample
{
//Database Team Class
public class DBTeam
{
public void PerformDatabaseWork()
{
Console.WriteLine("Database Team performing DB related Works here!!");
}
}
//Backedn Team Class
public class BackendTeam
{
public void PerformBackendWork()
{
Console.WriteLine("Backend Team handling backend related Works here!!");
}
}
//Fontend Team class
public class FrontendTeam
{
public void PerformFrontendWork()
{
Console.WriteLine("Frontend Team performing Frontend related Works here!!");
}
}
//Main Program4
public class Program
{
static void Main(string[] args)
{
//Instantiate teams
DBTeam dbteam = new DBTeam();
BackendTeam backendteam = new BackendTeam();
FrontendTeam frontendteam = new FrontendTeam();
//Each team performing their specific task
dbteam.PerformDatabaseWork();
backendteam.PerformBackendWork();
frontendteam.PerformFrontendWork()
}
o/p: Database Team performing DB related Works here!!
Backend Team handling backend related Works here!!
Frontend Team performing Frontend related Works here!!
Open and Closed Principle (OCP)
Simply put, Open means open for extension, and Closed means closed for modification. The OCP principle states that a class should be open to adding new functionality but closed to altering existing code.
using System;
namespace OCPExaple
{
//Base class for Mobile
public class Mobile
{
public virtual void Features()
{
Console.WriteLine("Basic mobile features: Calling and Messaging.");
}
}
//Update 1: Adding Camera Feature
public class CameraUpdate : Mobile
{
public ovrride void Features()
{
base.Features(); // Retain Existing features
Console.WriteLine("Added features: Camera functionality.");
}
}
// Update 2: Adding Internet Browsing Feature
public class InternetUpdate : Mobile
{
public ovrride void Features()
{
base.Features(); // Retain Existing features
Console.WriteLine("Added features: Internet Browsing.");
}
}
public class Program
{
static void Main(string[] args)
{
//Basic mobile
Mobile mobile = new Mobile();
mobile.Features();
Cosole.WriteLine("\n-----After Updates-----");
//Mobile With CameraUpdates
Mobile cameraUpdates = new CameraUpdate();
cameraUpdates.Features();
//Mobile WIth InstantiateUpdates
Mobile intenetUpdates = new InstantiateUpdates();
intenetUpdates.Features();
}
}
}
O/P: Basic mobile features: Calling and Messaging.
-----After Updates-----
Basic mobile features: Calling and Messaging.
Added features: Camera functionality.
Basic mobile features: Calling and Messaging.
Added features: Internet Browsing.
Liskov Substitution Principle (LSP)
If you have a parent class and a child class, you should be able to use the child class wherever the parent class is expected without breaking the program.
using System;
namespace LSPExample
{
//Base Class : Headset
public abstract class Headset
{
public abstract void PaySound();
}
//SubCLass: EarBuds
public class EarBuds : Headset
{
public override void PaySound()
{
Console.WriteLine("Playing sound through earbuds!");
}
}
//SubCLass: Wired Headset
public class WiredHeadset : Headset
{
public override void PaySound()
{
Console.WriteLine("Playing sound through a Wired Headset!");
}
}
//SubCLass: Bluetooth Headset
public class BluetoothHeadset : Headset
{
public override void PaySound()
{
Console.WriteLine("Playing sound through a Bluetooth Headset!");
}
}
public class Program
{
static void Main(string[] args)
{
// Using Base class reference for all types of headsets
Headset earbuds = new EarBuds();
Headset wiredheadset = new WiredHeadset();
Headset bluetoothheadset = new BluetoothHeadset();
//Substituiton: Each subclass can replace the base class
earbuds.PaySound();
wiredheadset.PaySound();
bluetoothheadset.PaySound();
}
}
}
O/P: Playing sound through earbuds!
Playing sound through a Wired Headset!
Playing sound through a Bluetooth Headset!
Interface Segregation Principle (ISP)
Simply say, a class or entity should not be forced to implement methods it does not use.Dependency Inversion Principle: Instead of directly relying on specific implementations, classes should depend on interfaces or abstract classes.Abstractions should not depend on details. Details should depend on abstractions.
// Define smaller, specific interfaces
public interface IPowerControl
{
void PowerOn();
void PowerOff();
}
public interface ITemperatureControl
{
void IncreaseTemperature();
void DecreaseTemperature();
}
public interface IChannelControl
{
void ChangeChannel(int channelNumber);
}
// Implement the interfaces based on the remote type
// AC Remote
public class ACRemote : IPowerControl, ITemperatureControl
{
public void PowerOn()
{
Console.WriteLine("AC is turned ON.");
}
public void PowerOff()
{
Console.WriteLine("AC is turned OFF.");
}
public void IncreaseTemperature()
{
Console.WriteLine("AC temperature increased.");
}
public void DecreaseTemperature()
{
Console.WriteLine("AC temperature decreased.");
}
}
// TV Remote
public class TVRemote : IPowerControl, IChannelControl
{
public void PowerOn()
{
Console.WriteLine("TV is turned ON.");
}
public void PowerOff()
{
Console.WriteLine("TV is turned OFF.");
}
public void ChangeChannel(int channelNumber)
{
Console.WriteLine($"TV channel changed to {channelNumber}.");
}
}
// Testing the remotes
class Program
{
static void Main(string[] args)
{
// AC Remote
ACRemote acRemote = new ACRemote();
acRemote.PowerOn();
acRemote.IncreaseTemperature();
acRemote.PowerOff();
Console.WriteLine();
// TV Remote
TVRemote tvRemote = new TVRemote();
tvRemote.PowerOn();
tvRemote.ChangeChannel(101);
tvRemote.PowerOff();
}
}
O/P: AC is turned ON.
AC temperature increased.
AC is turned OFF.
TV is turned ON.
TV channel changed to 101.
TV is turned OFF.
Dependency Inversion Principle (DIP)
Instead of directly relying on specific implementations, classes should depend on interfaces or abstract classes. Abstractions should not depend on details. Details should depend on abstractions.
using System;
namespace DIPExample
{
// 1. Abstraction
public interface INotificationService
{
void Send(string message);
}
// 2. Concrete Implementations
public class EmailNotification : INotificationService
{
public void Send(string message)
{
Console.WriteLine($"Email sent: {message}");
}
}
public class SMSNotification : INotificationService
{
public void Send(string message)
{
Console.WriteLine($"SMS sent: {message}");
}
}
// 3. High-Level Class
public class NotificationManager
{
private readonly INotificationService _notificationService;
public NotificationManager(INotificationService notificationService)
{
_notificationService = notificationService;
}
public void Notify(string message)
{
_notificationService.Send(message);
}
}
// 4. Test the Code
class Program
{
static void Main(string[] args)
{
// Use EmailNotification
INotificationService emailService = new EmailNotification();
NotificationManager emailManager = new NotificationManager(emailService);
emailManager.Notify("Welcome to our platform!");
Console.WriteLine();
// Use SMSNotification
INotificationService smsService = new SMSNotification();
NotificationManager smsManager = new NotificationManager(smsService);
smsManager.Notify("Your OTP is 123456.");
}
}
}
O/P: Email sent: Welcome to our platform!
SMS sent: Your OTP is 123456.