Thursday, September 10, 2015

Interface Segregation Principle in .NET

In today's post, I will be discussing the "I" in the SOLID principles.

Interface Segregation Principle (ISP)

According to this principle, no client should be forced to implement methods which it does not use. In other words, if you are implementing an interface and you use only a few of the methods out of the interface and don't need other methods, then you are violating the ISP. In such a scenario, the contract needs to be broken down into thin ones i.e. each interface should be more specific.




Interface Segregation Principle


Let's take a look at an example to better understand this.
Suppose you have an interface like this:

public interface IDataStore

{

     void Open();

     void Close();

     Result ExecuteQuery();
}

And, you have a DBStore class which implements the interface like this:

public class DBStore : IDataStore

{

        public void Open()

        {

            //db connection open

        }



        public void Close()

        {

            // db connection close

        }



        public Result ExecuteQuery()

        {

            //execute query code

        }

}


The clients who use the IDataStore interface can connect to the DB and execute queries and close connections. So everything is fine till this point.
Now, suppose you have a new requirement to be able to read file data. You might want to add a new method to your interface like this:

    public interface IDataStore

    {

        void Open();

        void Close();

        Result ExecuteQuery();

        Result ReadFile();

    }


And add a new class FileStore like this:

public class FileStore : IDataStore

{

        public void Open()

        {

            //open file connection

        }



        public void Close()

        {

            //close file connection

        }



        public Result ExecuteQuery()

        {

            throw new NotImplementedException();

        }



        public Result ReadFile()

        {

            //read file 

        }

}


And the DBStore class might look like this:

public class DBStore : IDataStore

{

        public void Open()

        {

            //db connection open

        }



        public void Close()

        {

            // db connection close

        }



        public Result ExecuteQuery()

        {

            //execute query code

        }



        public Result ReadFile()

        {

            throw new NotImplementedException();

        }

}

So we see that FileStore class has to implement ExecuteQuery() method which it doesn't need. Similarly, DBStore class needs to implement the ReadFile() method which it doesn't need. So it's a violation of Interface Segregation Principle.

We can segregate the interfaces into smaller ones like this:

public interface IDataStore

{

        void Open();

        void Close();

}



public interface IDBStore : IDataStore

{

        Result ExecuteQuery();

}



public interface IFileStore : IDataStore

{

        Result ReadFile();

}

And as a result, the FileStore class will look like this:

public class FileStore : IFileStore

{

        public void Open()

        {

            //open file connection

        }



        public void Close()

        {

            //close file connection

        }



        public Result ReadFile()

        {

            //read file 

        }

}


And DBStore class will look like this:

public class DBStore : IDBStore

{

        public void Open()

        {

            //db connection open

        }



        public void Close()

        {

            // db connection close

        }



        public Result ExecuteQuery()

        {

            //execute query code

        }

}

As a result of this, no class needs to implement interface methods which it doesn't need.

Conclusion

This principle is quite practical and makes the code cleaner and easy to use. If you see a bunch of "throw new NotImplementedException()" then probably you are violating the Interface Segregation Principle and need some refactoring. Having smaller cohesive interfaces is what we should strive for.

For future updates to my weekly blog, please subscribe to my blog via the "Subscribe To Weekly Post" feature at the right and follow me on Twitter. Until then Happy Coding :)


(Image Ref: here)

3 comments:

  1. Thanks for the explanation. I've always struggled with this portion of the SOLID principles.

    ReplyDelete
  2. > According to this principle, no client should be forced to implement methods which it does not use.

    Wrong from the first sentence.

    ISP is "no client should be forced to *depend on* methods which it does not use."

    To understand why ISP was created, you need to understand how the C++ compiler works, especially in regards to header files. If you have an overly large class (i.e. a god object) that you can't break up then any changes to can cascade throughout your project, significantly increasing build times.

    By breaking up that class's header file into several smaller header files, other classes can include just the subset they actually use. Since these other classes don't need to be recompiled unless the header they reference is actually updated, you reduce the number of unnecessary compilations when working on unrelated features.

    In .NET the unit of compilation is the assembly, not individual classes, so the Interface Segregation Principle doesn't actually apply.

    However, you can get many of the same benefits as ISP by segregating your classes into multiple assemblies.

    ReplyDelete
  3. Hi Jonathan, Thanks for the nice comment and letting us know your viewpoint with C++ context. I have not worked in large projects in C++ with use case you mentioned. So it's good to know.
    However, in .NET context, this principle is more about code readability and maintenance instead of performance. By segregating the interfaces, we are making it more readable and less prone to exceptions (as shown in the example above). Thanks.

    ReplyDelete