Learning The S.O.L.I.D Programming Principles: Interface segregation principle [Part – V]

History:

In our previous posts we learned ‘What is S.O.L.I.D. Programing Principles’ and a detailed explalantion with code of
Single Responsibility Principle, Open/closed Principle and Liskov Substitution Principle.


S.O.L.I.D. is an acronym introduced by Michael Feathers as:

  1. S for SRP: Single responsibility principle
  2. O for OCP: Open/closed principle
  3. L for LSP: Liskov substitution principle
  4. I for ISP: Interface segregation principle
  5. D for DIP: Dependency inversion principle
  • Single Responsibility Principle says class should have single responsibility. In reference to this I would say “A class should have single responsibility”.
    Lets dive into ocean – can we read this like “a class should not design to do multiple activities”.
  • Open/Closed Principle says “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”.
  • Liskov Substitution Principle says “if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substituted for objects of type T)
    without altering any of the desirable properties of that program (correctness, task performed, etc.)”

Learning S.O.L.I.D is very vast topic and can’t possible to explore in one-shot. I divided this into following parts:

Introduction

In this whole article, we will learn Interface Segregation Principle in details with example.

Here is a definition from wiki:

“No clients should be forced to implement methods, which it does not use and the contract should be broken into small and more specific intefaces.”

Interface Segregation Principle

Lets read above definition as: “as a client why should I implement 9-methods of interface when I need only 3-methods?”.

This is also similar to High Cohesion Principle of GRASP.

Lets explore this with an example:
First of all go back and take a look into code example discussed in LSP, there some of our databases
are getting saved after validation. Now, think a scenario there are more dataabses and for these additional databases we require a report in other words
new databases needs to be read and saved.

In very first instance, I can think to add a new method to interface IRepository (which can read data or geenrate report).

public interface IRepository
{
	void Save();
	void Generate();
}

Do you think, above approach is good?
Think, think and again think…. 🙂

By adding new method to an existing interface, we are forcing to use new method to all those classes, who are implementing this interface. But those classes are not
supposed to use newly added method. So, my ProdDB class look like:

public class ProdDB : DataBase
{
	public override bool IsValid(ServerData data, SourceServerData sourceData)
	{
		return base.IsValid(data, sourceData);
	}

	public override void Save()
	{
		//logic to save data
		base.Save();
	}
	
	public override void Generate()
	{
		//Report generation logic
	}
}

But actually, ProdDB class does not require to Generate report, but with above implementation this class have to implement new Generate() method.
Here, we are forcing our class to implement that method, which this class does not want.

So, we are not following Interface Segregation Principle in our above code [go top and read ISP definition :)].

What is the solution for this problem?

First, try to segregate our IRepository interface.

public interface IReport:IRepository
{
	void Generate();
}

To segregate, I created another IReport interface with new method Generate(). Now, we have two separate interfaces IRepository and IReport.
Lets create a new class, which is meant for those clients, who wants to generate report:

public class DataBaseReport : IReport
{
	public void Save()
	{
		var dataBase = new DataBase();
		dataBase.Save(); // we want to save data
	}

	public void Generate()
	{
		//implement report generation logic here
	}
}

At this point, we have two different classes DataBase and DataBaseReport, one is for those clients who dont want to generate report and another is
who wants to generate report 🙂

So, our execute method would look like:

public void Execute()
{
	//Old client implementation
	IRepository repository = new DataBase();
	repository.Save();

	
	//implementation for new clients who want to generate report
	IReport report = new DataBaseReport();
	report.Generate();
}

You can see, how we resolved the problem. This solution will very useful when millions of clients need different things while our existing clients don’t 🙂

How to download source-code?

You can download complete souce code of examples used in this article from GitHub: Learning Solid.