June 29, 2020 shalin

Why SOLID principles are necessary for any Enterprise Software?

SOLID Principles is a an acronym used as a coding standard. Robert Martin introduced it first. It is used across the object-oriented design family. All developers should have a clarity for developing software in the right way to avoid a bad design. When applied appropriately it makes your code easier to read, more extendable and logical. A software with a bad design has inflexible code. Small changes if introduced to such software may break existing code or cause bugs. Design principles and patters prove a lot of significance in any software’s scalability, maintainability. It may take time to understand SOLID principles but they improve code quality and provide component reusability making future integrations easier. Extending functionality in your existing app without rewriting code at all tiers will be possible through these principles.

Why many software applications fail due to bad code?

All softwares which are not built with future vision of type of change requests or system scalability will die. Following are design flaws that cause dent in software in the long run:

  1. Put more stress on Classes by assigning more responsibilities to them. (A lot of functionality not related to a class put together.)
  2. Forcing the classes to depend on each other. If classes are dependent on each other (in other words tightly coupled), then a change in one will surely affect the other.
  3. Spreading duplicate and redundant code in the system/application.
  4. Hardcoding url, constants, keys etc. as well as habit of declaring static code everywhere.

The right way to avoid all the above is SOLID way. Lets see what it actually means:

        S: Single Responsibility Principle (SRP)
        O: Open closed Principle (OSP)
        L: Liskov substitution Principle (LSP)
        I: Interface Segregation Principle (ISP)
        D: Dependency Inversion Principle (DIP)

Single Responsibility Principle (SRP)

This SOLID Principle means “A Class should only have a single responsibility“. All its method should relate to function. Class should only have one reason to change; the more entities in the class, the more difficult it becomes to achieve this. Only change in 1 part of software should be all required to affect the class functionality. So we need to build a structure of software in such a way that classes should cover only one responsibility or functionality.

To understand this let’s consider, We implement the methods for a Company. Now we also want logging, error handling, emailing etc, kind of activities then we must implement all of these into different classes.
For an instance below I have not followed this rule:

public class Company

{

     public void AddCompany()

     {

         //Implementation

         MailMessage mailMessage = new MailMessage(“EMailFrom”“EMailTo”“EMailSubject”“EMailBody”);

         this.SendEmail(mailMessage);

     }

     public void DeleteCompany()

     {

         //Implementation

     }

     public void SendEmail(MailMessage mailMessage)

     {

         //Implementation

     }

}

Now if I want to make my code with Single Responsibility Principle I will create a separate class for employee and Email like below:

public class Company

{

public void AddCompany()

{

     //Implementation

     MailMessage mailMessage = new MailMessage(“EMailFrom”“EMailTo”“EMailSubject”“EMailBody”);

     Emailing emailing = new Emailing();

     emailing.SendEmail(mailMessage);

}

public void DeleteCompany()

{

     //Implementation

}

}

public class Emailing

{

public void SendEmail(MailMessage mailMessage)

{

     //Implementation

}

}

 If we write the database logic, business logic as well as the display logic in a single class, then our class performs multiple responsibilities. So it gets very difficult to change one responsibility without breaking other functionalities. Also we may face difficulties with testing and duplication of logic. What if I want to send an email for an Employee in future requirements ? That’s why we recommend to have separate code for each purpose.

Open closed Principle (OSP)

Open closed Principle is defined as “Software entities such as modules, classes, functions, etc. should be open for extension, but closed for modification“. The first thing is “Open for extension” which means new functions, classes, modules can inherit existing code when new requirements are generated and “Closed for modification” which means we are allowing its behavior to be extended without modifying existing source code. In a simple way we can say that we should not change already working functionality, it may affect the entire feature of that module.

Easiest way to implement the Open-Closed Principle

  •         Use derived classes that inherit from base class
  •         Allow access to use original classes with interface
  •         Create new method for new feature so better to us derived class instead of base class

Not following this principle brings problem with existing module and its class maintenance becomes difficult. In addition with every change in existing code we have to test our entire code every time. This also breaks our first principle Single Responsibility Principle.

Example

public class Event

{

public int GetDiscount(int input, DiscountType discountType)

{

     int amount = 0;

     if (discountType == DiscountType.TypeOne)

     {

         amount = input – 20;

     }

     if (discountType == DiscountType.TypeTwo)

     {

         amount = input – 40;

     }

     return amount;

}

}

Here we are checking discount types implementing a business Logic with if else conditions so if in future we have another discount for new events then we need to change the logic for this method.

public class Event

{

public virtual int GetDiscount(int amount)

{

     int regularDiscount = 10;

     return amount – regularDiscount;

}

}

public class DiscountOne : Event

{

public override int GetDiscount(int input)

{

     return base.GetDiscount(input) – 20;

}

}

public class DiscountTwo : Event

{

public override int GetDiscount(int input)

{

     return base.GetDiscount(input) – 40;

}

}

Using this Open-Closed Principle we can inherit from base class and use custom logic for. Here if we are adding more into existing code, we dont need to worry about other functionalities and let it remain as it is.

Liskov substitution Principle (LSP)

This principle tells “Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program“. It also defines some guidelines for maintaining inheritor substitution. Passing an object’s inheritor in place of the base class shouldn’t break any existing functionality in the called method. You should be able to substitute all implementations of a given interface with each other.

public class Program

{

static void Main(string[] args)

{

     A objA = new B();

     Console.WriteLine(objA.GetValue());

}

}

public class A

{

public virtual int GetValue()

{

     return 10;

}

}

public class B : A

{

public override int GetValue()

{

     return 20;

}

}

In this example we have used inherited class A and override method GetValue() so at this point class B cannot be replaced by class A and it returns value from class B. In this case we need to have a generic base class so we can use sub class and it will behave correctly.

public class Program

{

static void Main(string[] args)

{

     C objA = new A();

     C objB = new B();

     Console.WriteLine(objA.GetValue());

     Console.WriteLine(objB.GetValue());

}

}

public abstract class C

{

public abstract int GetValue();

}

public class A :C

{

public override int GetValue()

{

     return 10;

}

}

public class B : C

{

public override int GetValue()

{

     return 20;

}

}

Interface Segregation Principle (ISP)

This Principle in SOLID emphasizes as “Clients should not be forced to implement any methods they don’t use”. Rather than one fat interface, numerous little interfaces are preferred based on groups of methods with each interface serving one sub module. Do not create methods which will not be used or implemented.

If we need to implement the functionality and have a common method implementation from the interface or abstract class but we don’t use all the methods from that then create a separate interface and implement individually. 

public interface AppService

{

void Insert();

void Update();

void Delete();

}

public class Employee : AppService

{

public void Insert()

{

     Console.WriteLine(“Created”);

}

public void Update()

{

     Console.WriteLine(“Updated”);

}

public void Delete()

{

     Console.WriteLine(“Deleted”);

}

}

public class Company : AppService

{

public void Insert()

{

     Console.WriteLine(“Created”);

}

public void Update()

{

     Console.WriteLine(“Updated”);

}

public void Delete()

{

     throw new NotImplementedException();

}

}

Here we have implementation of AppService interface into employee and company where we don’t want to implement Delete method for Company then we can use another interface service that only provides Delete method to implement like this,

public interface AppService

{

void Insert();

void Update();

}

public interface DeleteService

{

void Delete();

}

public class Employee : AppService, DeleteService

{

public void Insert()

{

     Console.WriteLine(“Created”);

}

public void Update()

{

     Console.WriteLine(“Updated”);

}

public void Delete()

{

     Console.WriteLine(“Deleted”);

}

}

public class Company : AppService

{

public void Insert()

{

     Console.WriteLine(“Created”);

}

public void Update()

{

     Console.WriteLine(“Updated”);

}

}

 So as per SOLID we can only implement required services and no need to write invalid code. We have split that big interface into three small interfaces. Each interface now has some specific purpose.

Dependency Inversion Principle (DIP)

The Dependency Inversion Principle says that “Program to an interface, not to an implementation“. It means high-level modules/classes should not depend on low-level modules/classes. Both should depend upon abstractions. Secondly, abstractions should not depend upon details. Dependency Inversion means to depend on abstractions instead of concrete types.

If one class knows implementation of another class then it will raise the risk of changing one will affect another. So it should both depend on abstraction. As a result we must keep these high-level and low-level modules/classes loosely coupled as much as possible.

public class EmployeeController

{

BusinessLogic service;

public EmployeeController(BusinessLogic _service)

{

     service = _service;

}

public List<Employee> GetAllEmployee()

{

     return service.GetEmployees();

}

}

public class BusinessLogic

{

public List<Employee> GetEmployees()

{

     //Implementation

}

}

Here we are using BusinessLogic class which gives access to data layer. It also gives implementation of actual logic of employee where EmployeeController is higher level class and BusinessLogic is lower level class. Therefore, class/module should not depend on the concrete EmployeeDataAccess class/module, instead, both the classes should depend on abstraction or interface. For instance as below:

public class EmployeeController

{

     IBusinessLogic service;

     public EmployeeController(IBusinessLogic _service)

     {

         service = _service;

     }

     public List<Employee> GetAllEmployee()

     {

         return service.GetEmployees();

     }

}

public interface IBusinessLogic

{

     List<Employee> GetEmployees();

}

public class BusinessLogic: IBusinessLogic

{

     public List<Employee> GetEmployees()

     {

         //Implementation

     }

}

Now both EmployeeController and BusinessLogic classes are loosely coupled so it doesn’t depend on concrete BusinessLogic class.

Conclusion

Indeed, the SOLID principles are generally overlapping in their effects upon sustainable computer code. As the process of writing software has always evolved from the theoretical realm into a true engineering discipline.

Hope I gave you a good picture about how to implement SOLID principles. In case you are looking for additional consulting, or you need to Hire a Developer then we will be happy to help you. For queries, mail your problem or requirement at [email protected] or skype me @ shalin.jirawla

, , , , , , , , , , , , , , , , , , , ,
Visit Us On TwitterVisit Us On Facebook