Thursday, 9 July 2020

SOLID Design Principles in C# - Dependency Inversion Principle (DIP)


Definition of the Dependency Inversion Principle?

High-level modules should not depend on low-level modules. Both should depend on the abstraction.
Abstractions should not depend on details. Details should depend on abstractions.

What is high-level module?

A high-level module is a module which depends on other modules.
Ex: Direct Dependency - A concrete class depends on other concrete classes.

What is low-level module?

A low-level module is a module which depends on abstraction (abstract classes/interfaces).
Ex: Inverted Dependency - Concrete class depends only on abstract class or interfaces.

Lets consider a banking transaction scenario where we provide the feature of fetching transcation between the given dates.


  
In the above code both Account and TransactionService are concrete classes and Account class is high-level class which is depending on the low-level module TransactionService. Account  class is having strong tightly coupled dependency with TransactionService. Testing frameworks configuration and code maintenance is typical. Let's refactor the code by adding the Dependency Inversion Principle rules on the above code.

First let's introduce a new interface which acts as a bridge between Account and TransactionService classes. 


Implement the interface in TransactionService class

Finally use the ITransactionService in Account class

Here now the Account class is depending on the ITransactionService which is an abstraction for both Account and TransactionService.  

Advantages:

- It breaks the tight coupling by introducing the abstraction.
- Code maintenance is easier as we are stabling the modules.
- Test Driven Development is easier with the testing frameworks.
- Code-Re-usability.
- Reduces the risk.

Tuesday, 7 July 2020

SOLID Design Principles in C# - Interface Segregation Principle (ISP)




Definition of the Interface Segregation Principle?

Clients should not be forced to depend upon interfaces that they do not use.

The aim of the Interface Segregation Principle is to divide a fat interface into multiple small interfaces.

Assume that we have a requirement from client which he needs a blog service which will provides a following features.
1. Blog post should have Create, Update, Delete and Archive features.
2. Supports posting along with HashTags, and search post by HashTags.
3. Capture user location, and provide a provision to delete and update location.
 
So we have added all these features in one interface tested and delivered to client. 

Blog Post Interface

public interface IBlogPost
{
void CreateNew(Post postDetails);
void UpdatePost(Post postDetails);
void DeletePost(int postId);
void Publish(int postId);
void ArchivePost(int postId);
void UpdateHashTags(int postId, string[] hashTags);
List<Post> SearchByHashTag(string hashTag);
void DeleteHashTags(int postId, string[] hashTags);
void UpdateLocation(int postId, string location);
void DeleteLocation(int postId);
}

Assume that we have marketed our product and given this to multiple clients.

Can you imagine for a minute what challenges we face in future with this fat Interface?

1. Should all clients are forced to implement all the methods in this interface or not?
2. If any one client approaches us and said he do not want location feature for him. What challenges do you face?
3. If any client asked to remove location and add mobile and email address features to the post What challenges do you face?
4. What if any of the feature is not working among them in the interface?
5. How maintenance will be taken care in the production.
6. What testing efforts do we need put if we modify this Interface, shouldn't we test all the features along with the intended one?
7. Is it not violating Single Responsibility Principle?

This is where Interface Segregation Principle will help in solving the above problems. Lets refactor code by considering the high cohesion methods and separate them into the multiple interfaces.

Interface Segregation Principle Implementation

public interface IBlogPostService
{
void CreateNew(Post postDetails);
void UpdatePost(Post postDetails);
void DeletePost(int postId);
void Publish(int postId);
void ArchivePost(int postId);
}

public interface IHashtagsService
{
void UpdateHashTags(int postId, string[] hashTags);
List<Post> SearchByHashTag(string hashTag);
void DeleteHashTags(int postId, string[] hashTags);
}

public interface ILocationService
{
void UpdateLocation(int postId, string location);
void DeleteLocation(int postId);
}

We have segregated the fat interface into multiple small interfaces with the greater cohesion. Let's re look at the issues we considered above. 
   
1. Should all clients are forced to implement all the methods in this interface or not?
    - Clients can utilize the required services and leave the rest.

2. If any one client approaches us and said he do not want location feature for him. What challenges do you face?
    - In this case client won't even come to us, because he has a provision to avoid ILocationService.

3. If any client asked to remove location and add mobile and email address features to the post What challenges do you face?
    - We can add new Interfaces which supports the phone and email features. Ex: IMobileService, IEmailService.

4. What if any of the feature is not working among them in the interface?
    - Rather than modifying the code in fat interface we can fix the code only in the service which has the issues.

5. How maintenance will be taken care in the production.
    - Maintenance is less compared to maintaining the fat interface. How? you can check the 4th point. 

6. What testing efforts do we need put if we modify this Interface, shouldn't we test all the features along with the intended one?
    - We do not need to test all services which we provided, testing the services which we fixed the issues is enough.

7. Is it not violating Single Responsibility Principle?
    - As every service is segregated with the high cohesion it is satisfying the Single Responsibility Principle.

Conclusion: 
Interface Segregation Principle will not force the client to use all the methods in interfaces, he has the provision to utilize and avoid as per his needs.

Please check my below posts on other SOLID principles for better understanding.


SOLID Design Principles in C# - Open/Closed Principle (OCP)

The open-closed principle definition by Bertrand Meyer

“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

Open for extension: Entities should allow themselves to be extended.

Closed for modification: Once the entities are defined those should not be touched later on.

Once you implemented the base class you should not modify the functionality, if you change it, it will end up changing the behavior of the derived classes. It leads to code changes in all derived classes as well, you will mess up whole derived classes code every time you change the base class. Code maintenance will become difficult, and the code won't be flexible for the further changes.

Open Closed Principle helps in avoid touching the base class and allows the class to extends the functionality of it's by implementing the new class.

Real Life Example:

 - An adapter is closed for modification it provides flexibility to use the ports.

- So you can use the extension board and use it for different purpose.

- You can charge the mobile, laptop, power banks with the extension cables.


Lets take an example of salary processing in a company which sets the max salary based on the designation.

Class which violates Open/Closed Principle

public void GetSalaryLimit(string grade)
{
    if(grade=="A")
    {
        MaxSalary = 2000000;
    }
    else if(grade=="B")
    {
        MaxSalary = 1500000;
    }
    else if(grade=="C")
    {
        MaxSalary = 1200000;
    }
        else if(grade=="D")
    {
        MaxSalary = 1000000;
    }
}

The above code is not following the Open Closed Principle. If the company is introduced the new grades tomorrow we need to make changes in the class. It itself is saying the class is open for the modification. 

Let's fix the code by applying the Open Closed Principle. Inheritance is the only way we can extend the functionality of a class. Lets create an interface ISalaryLimit which will extended by GradeA, GradeB, GradeC, GgradeD employees.

Open/Closed Principle Implementation

public interface ISalaryLimit
{
     void SetMaxSalaryLimit();
}

public class GradeA : ISalaryLimit
{
    public void SetMaxSalaryLimit()
    {
        MaxSalary = 2000000;
    }
}

public class GradeB : ISalaryLimit
{
    public void SetMaxSalaryLimit()
    {
        MaxSalary = 1500000;
    }
}

public class GradeC : ISalaryLimit
{
    public void SetMaxSalaryLimit()
    {
        MaxSalary = 1200000;
    }
}

public class GradeD : ISalaryLimit
{
    public void SetMaxSalaryLimit()
    {
        MaxSalary = 1000000;
    }
}

In the above example we have introduced new Interface which is fixed and from this we extended four classes. If tomorrow company is introduces a new grade we can introduce a new class which extends the ISalaryLimit interface. 

Please check my below posts on other SOLID principles for better understanding

Single Responsibility Principle (SRP)
https://dotnetcookie.blogspot.com/2019/12/solid-design-principles-in-c-single.html