Theme-Logo
  •   .Net
    •   C Sharp(C#)
    •   Web API
    •   Micro Services
    •   ASP.Net
    • ASP.Net MVC
    • .Net Core
  •   Database
    • SQL Server
    • Oracle
    • PostgreSQL
  •   jQuery
    • jQuery Tutorials
    • jQuery Plugins
    • jQuery UI
    • More on jquery
  •   Tutorials
    • Microservices Tutorials
    • DotNet Core Tutorials
    • PostgreSql Tutorials
SOLID Principles
In Object Oriented Programming (OOP), SOLID is an acronym, introduced by Michael Feathers, for five design principles used to make software design more understandable, flexible, and maintainable. These principles are a subset of many principles promoted by Robert C. Martin.
There are five S.O.L.I.D. principles:

  • S stand for SRP : Single Responsibility Principle (SRP)
  • O stand for OCP : Open Closed Principle (OCP)
  • L stand for LSP : Liskov Substitution Principle (LSP)
  • I stand for ISP : Interface Segregation Principle (ISP)
  • D stand for DIP : Dependency Inversion Principle (DIP)
  • Advantage of SOLID Principles

  • Reduce tight coupling
  • Increase readability, extensibility and maintenance
  • Achieve reduction in complexity of code
  • Reduce error and implement Reusability
  • Achieve Better testability of code
  • Single Responsibility Principle (SRP)
    A class should have only one reason to change.

    Robert C. Martin gave this definition in his book Agile Software Development, Principles, Patterns and Practices and later republished in the C# version of the book Agile Principles, Patterns and Practices in C#. In layman terminology, this means that a class should not be loaded with multiple responsibilities and a single responsibility should not be spread across multiple classes or mixed with other responsibilities. The reason is that more changes requested in the future, the more changes the class need to apply.

    Single Responsibility Principle (SRP) is one of the five SOLID principles which guide developers as they write code or design an application. In simple terms, a module or class should have a very small piece of responsibility in the entire application. Or as it states, a class/module should have not more than one reason to change.

    If a class has only a single responsibility, it is likely to be very robust. It’s easy to verify its working as per logic defined. And it’s easy to change in class as it has single responsibility. The Single Responsibility Principle provides another benefit. Classes, software components and modules that have only one responsibility are much easier to explain, implement and understand than ones that give a solution for everything. This also reduces number of bugs and improves development speed and most importantly makes developer’s life lot easier.

    Let’s take a scenario of Garage service station functionality. It has 3 main functions; open gate, close gate and performing service. Below example violates SRP principle. The code below, violates SRP principle as it mixes open gate and close gate responsibilities with the main function of servicing of vehicle.
                         public class GarageStation
                                {
                                 public void DoOpenGate()
                                 {
                                 //Open the gate functinality
                                 }
     
                                 public void PerformService(Vehicle vehicle)
                                 {
                                 //Check if garage is opened
                                 //finish the vehicle service
                                 }
     
                                 public void DoCloseGate()
                                 {
                                 //Close the gate functinality
                                 }
                                }
                                
    We can correctly apply SRP by refactoring of above code by introducing interface. A new interface called IGarageUtility is created and gate related methods are moved to different class called GarageStationUtility.
                            public class GarageStation
                            {
                            IGarageUtility _garageUtil;
    
                            public GarageStation(IGarageUtility garageUtil)
                            {
                            this._garageUtil = garageUtil;
                            }
                            public void OpenForService()
                            {
                            _garageUtil.OpenGate();
                            }
                            public void DoService()
                            {
                            //Check if service station is opened and then
                            //finish the vehicle service
                            }
                            public void CloseGarage()
                            {
                            _garageUtil.CloseGate();
                            }
                            }
                            public class GarageStationUtility : IGarageUtility
                            {
                            public void OpenGate()
                            {
                            //Open the Garage for service
                            }
    
                            public void CloseGate()
                            {
                            //Close the Garage functionlity
                            }
                            }
    
                            public interface IGarageUtility
                            {
                            void OpenGate();
                            void CloseGate();
                            }
     
    Open Closed Principle (OCP)
    Software entities such as classes, modules, functions, etc. should be open for extension, but closed for modification

    Above statement means, any new functionality should be implemented by adding new classes, attributes and methods, instead of changing the current ones or existing ones.

    The easiest way to apply OCP is to implement the new functionality on new derived (sub) classes that inherit the original class implementation.

    Another technique is to allow client to access the original class with an abstract interface.

    So, at any given point of time when there is a requirement change instead of making change in the existing functionality it’s always suggested to create new classes and leave the original implementation untouched.

    Liskov Substitution Principle (LSP)
    The Liskov Substitution Principle (LSP) states that "you should be able to use any derived class instead of a parent class and have it behave in the same manner without modification". It ensures that a derived class does not affect the behavior of the parent class, in other words, that a derived class must be substitutable for its base class. This principle is just an extension of the Open Close Principle.

    So, according to LSP we must ensure that new derived classes extend the base classes without changing their behavior.

    We should make sure that Clients should not know which specific subtype they are calling, nor should they need to know that. The client should behave the same regardless of the subtype instance that it is given. New derived classes just extend without replacing the functionality of old classes.

    Interface segregation Principle (ISP)
    The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.

    The Interface Segregation Principle (ISP) states that clients should not be forced to depend upon interfaces that they do not use. When we have non-cohesive interfaces, the ISP guides us to create multiple, smaller, cohesive interfaces. The original class implements each such interface. Client code can then refer to the class using the smaller interface without knowing that other members exist.

    When you apply the ISP, class and their dependencies communicate using tightly-focussed interfaces, minimising dependencies on unused members and reducing coupling accordingly. Smaller interfaces are easier to implement, improving flexibility and the possibility of reuse. As fewer classes share interfaces, the number of changes that are required in response to an interface modification is lowered. This increases robustness.

    Let's see below example to understand ISP

                                    public interface IOrder
                                        {
                                            void Purchase();
                                            void ProcessCreditCard();
                                        }
     
                                        public class OnlineOrder : IOrder
                                        {
                                            public void Purchase()
                                            {
                                                //Do purchase
                                            }
     
                                            public void ProcessCreditCard()
                                            {
                                                //process through credit card
                                            }
                                        }
     
                                        public class InpersonOrder : IOrder
                                        {
                                            public void Purchase()
                                            {
                                                //Do purchase
                                            }
     
                                            public void ProcessCreditCard()
                                            {
                                                //Not required for inperson purchase
                                                throw new NotImplementedException();
                                            }
                                        }
                                

    In the above example ISP is violated where ProcessCreditCard method is not required by InpersonOrder class but is forced to implement.

    Let us fix the violation.

                                public interface IOrder
                                    {
                                        void Purchase();
                                    }
     
                                    public interface IOnlineOrder
                                    {
                                        void ProcessCreditCard();
                                    }
     
                                    public class OnlineOrder : IOrder, IOnlineOrder
                                    {
                                        public void Purchase()
                                        {
                                            //Do purchase
                                        }
     
                                        public void ProcessCreditCard()
                                        {
                                            //process through credit card
                                        }
                                    }
     
                                    public class InpersonOrder : IOrder
                                    {
                                        public void Purchase()
                                        {
                                            //Do purchase
                                        }
                                    }
    
    Dependency Inversion Principle (DIP)

    Dependency Inversion Principle, provides us the technique to create loosely coupled concrete classes instead of high level classes.

    High-level modules/classes Should not depend upon low-level modules/classes. Both should depend upon abstractions. Secondly, abstractions should not depend upon details. Details should depend upon abstractions.

    To understand lets take an example of two classes one is ErrorLogWritter another is ErrorLogManager as shown below.

                                    public class ErrorLogWritter
                                    {
                                        public void WriteLog(string error)
                                        {
                                        System.IO.File.WriteAllText("c:\ErrorLog.log", error);
                                        }
                                    }
    
                                    public class ErrorLogManager
                                    {
                                        ErrorLogWritter writer = null;
                                        public void Notify(string msg)
                                        {
                                            if(!string.IsNullOrEmpty(msg))
                                            {
                                                writer = new ErrorLogWritter();
                                                writer.WriteLog(msg);
                                            }
                                       }
                                  }
    
                         

    In above code ErrorLogWritter class responsibility to write error into the ErrorLog.log file and ErrorLogManager have responsibility to log errors if any error raised with in the entire application. ErrorLogManager is high level class, which directly depends upon low level ErrorLogWritter concrete class. Here code will work fine. This design fails the dependency inversion principle. It will create tightly coupling between classes. With such classes, complex factor increases and understanding the code also increases.

    Moreover if client demands to send email for error log and sms error log intimation service etc. Again more complexity increases with two more new classes one for email and other for sms error log service as shown code below.

                                using System;
    
                                namespace SOLID
                                {
                                                public class ErrorLogWritter
                                                {
                                                    public void WriteLog(string error)
                                                    {
                                                    System.IO.File.WriteAllText("c:\ErrorLog.log", error);
                                                    }
                                                }
    
                                                public class SMSService
                                                {
                                                    public void WriteLog(string error)
                                                    {
                                                        //
                                                    }
                                                }
    
                                                public class EmailService
                                                {
                                                    public void WriteLog(string error)
                                                    {
                                                     //
                                                    }
                                                }
    
                                                public class ErrorLogManager
                                                {
                                                    ErrorLogWritter writer = null;
                                                    EmailService email = null;
                                                    SMSService sms = null;
                                                        public void Notify(string msg)
                                                        {
                                                                if(!string.IsNullOrEmpty(msg))
                                                                {
                                                                    writer = new ErrorLogWritter();
                                                                    email = new EmailService();
                                                                    sms = new SMSService();
                                                                    writer.WriteLog(msg);
                                                                    email.WriteLog(msg);
                                                                    sms.WriteLog(msg);
                                                                }
                                                       }
                                                }
                                  }
    
    
    Trending Post
    Dependency Injection in C#
    Inversion of controls (IoC)
    Design patterns
    Service-Oriented Architecture(SOA)
    Interview Questions and Answers
    What is SOLID Principles?
    What is Design patterns?
    What is tight coupling and loose coupling?
    Advantage of design patterns?
    What is difference between IoC and DI?
    About us

    DotNet Palace is a community platform created and maintained by The articles are mainly focused on Microsoft stack technologies like C#, ASP.Net, MVC, .Net Core, SQL Server and Oracle, PostgreSQL, SQLite etc. To improve the site's content you can send your valuable suggestions at info.dotnetpalace@gmail.com

    Quick links
  • SOLID Principles
  • Questions
  • OOPS Principles
  • DotNet Palace Tutorials
  • Privacy Policy
  • Terms and Condition