Understanding SOLID Principles

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.


Similar Articles