Interface Segregation Principle

It’s useless to sell a frying pan to someone who doesn’t cook. Don’t give him a chance to say I don’t cook. In programming world we call it throwing NotImplemented Exception.

An interface defines the contract of an object. Contract means, the functions an object has to perform. When a class implements an interface, it has to provide implementation of all the functions defined in interface.
ISP states, Clients should not be forced to depend upon interfaces that they do not use.
Always create smallest possible interfaces rather interfaces with huge number of methods (contracts). Classes that implement interfaces should not be forced to provide implementation of methods which are not useful for them. Problem arises when any client using such an interface would end up having functions it does not use.
Let me code
Let’s assume a scenario where we have to work with streams of data. For defining contract of a stream we create an interface IStream. A stream normally provides Read, Write and Clear functions.

    interface IStream
    {
        void Read();
        void Write();
        void Clear();
    }

This IStream is implemented by a Stream class.

    class Stream : IStream
    {
        public void Read()
        {
            // code to read stream
        }

        public void Write()
        {
            // code to write
        }

        public void Clear()
        {
            // clear the stream
        }
    }

Now the client using the IStream would use it as

    class SDCard
    {
        public IStream MemoryStream { get; set; }

        public void Read()
        {
            MemoryStream.Read();
        }

        public void Write()
        {
            MemoryStream.Write();
        }

        public void Format()
        {
            MemoryStream.Clear();
        }
    }

For this scenario, our interface is performing as per expectation. But in another scenario it is violating ISP. Think about a client which only performs write operation on streams e.g. a stream printer. So for a printer the Read function doesn’t make sense. This is how it should look like.

    class PrintableStream : IStream
    {
        public void Write()
        {
            // code to write
        }
    }

But hold on we cannot compile this code because it’s a must to implement all the functions of an interface. So what we do? Throwing a not implemented would work? Yes indeed it would but let’s have look at wiser approach.
Instead of creating one fat interface, let’s divide our interface into multiple interfaces. Introducing IReadable and IWriteable would do the trick.

    interface IReadableStream
    {
        void Read();
    }

    interface IWriteableStream
    {
        void Write();
        void Clear();
    }

Now our stream printer class can implement only IWriteableStream and provide only the functionality of writing the streams.

    class PrintableStream : IWriteableStream
    {
        public void Write()
        {
            // code to write
        }

        public void Clear()
        {
            // clear
        }
    }

While our Stream class can implement both interfaces.

class Stream : IReadableStream, IWriteableStream
    {
        public void Read()
        {
            // code to read stream
        }
        public void Write()
        {
            // code to write
        }

        public void Clear()
        {
            // clear the stream
        }
    }

Divide and conquer rather divide and resolve

Leave a Reply