DotNetCurry Logo

SOLID: Interface Segregation Principle (Part 4)

Posted by: Craig Berntson , on 3/7/2016, in Category Software Gardening
Views: 10764
Abstract: The Interface Segregation Principle (ISP) states that interfaces should be small and should contain only those methods or properties that are effectively required. We will explore ISP in this article.

Using interfaces is one of the best ways to allow extensibility and testability in your application. Using interfaces correctly is a key to making this happen well. That’s the point of the Interface Segregation Principle (ISP) of SOLID. This principle was created by “Uncle” Bob Martin and states “Clients should not be forced to depend on methods that they do not use.”

This article is published from the DNC Magazine for .NET Developers and Architects. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions

interface-segragation

Editorial Note: You can also read about Single Responsibility Principle, Open-Closed Principle and Liskov Substitution Principle .

You probably understand what an interface is, but do you know the meaning of segregation? Here in the US when we hear the word segregation, we think of a period of time when children of minority races were sent to their own schools. In other words, they were segregated or set apart.

We should look at a real example of how ISP is used effectively in the .NET Framework. One of the keys to making LINQ work is the IQueryable<T> interface and is defined as

public interface IQueryable<out T> : IEnumerable<T>, IQueryable, IEnumerable

As you can see, IQueryable<T> inherits from three other interfaces. Here’s what each interface does according to MSDN:

image

What this shows is that the functionality of iterating over a collection is separated (or segregated) from the functionality of evaluating queries. Furthermore, querying is separated between knowing and not knowing the data type of the data and iterating is separated between knowing and not knowing the data type of the collection.

When it comes to developing your own classes, think about the specific functionality needed and split that into different interfaces. The book “Adaptive Code Via C#” uses an example of the CRUD (Create, Read, Update, Delete) class. Rather than having a single ICRUD interface for this class, you may want to have ICreate, IRead, IUpdate, and IDelete. This makes it easier to swap out specific functionality, say from reading a database to reading a cache or maybe implementing CRQS, Command Response Query Separation, where commands to read the data are separated from commands to update the data. This satisfies the principle as classes that inherit the interfaces aren’t required to implement functionality they don’t need.

You should also beware of the dangers of creating the four interfaces, then having an ICRUD that inherits from each. That doesn’t solve the problems you want to avoid as it creates what Uncle Bob calls a “fat interface”.

When applying the ISP to software development, we separate interfaces into their functional areas. If this sounds like the Single Responsibility Pattern (SRP), you’re onto something. Where SRP is applied to classes and methods, ISP applies to interfaces.

This principle also enforces high cohesion, giving you better understanding and a more robust class and low coupling, which is more easier to maintain and more resistant to change (ie, less likely to introduce bugs).

Violating Interface Segregation Principle

Let’s turn to an example, first looking at code that violates ISP. This code has been in production for some time to send a message via email.

public interface IMessage
{
    IList<string> SendToAddress { get; set; }
    string Subject { get; set; }
    string MessageText { get; set; }
    bool Send();
}
 
public class EmailMessage : IMessage
{
    IList<string> SendToAddress { get; set; }
    string Subject { get; set; }
    string MessageText { get; set; }
 
    bool Send()
    {
        // Contact SMTP server and send message
    }
}

The team now needs to also send SMS or text messages and decides to leverage the existing interface.

public class SMSMessage : IMessage
{
    IList<string> SendToAddress { get; set; }
    string MessageText { get; set; }
    string Subject
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }
 
    bool Send()
    {
        // Contact SMS server and send message
    }
}

Because SMS doesn’t have a Subject, an exception is thrown. You can’t simply take out Subject because it’s required by the interface. It can get worse if the team decides to add CCToAddress.

public interface IMessage
{
    IList<string> SendToAddress { get; set; }
    IList<string> CCToAddress { get; set; }
    string Subject { get; set; }
    string MessageText { get; set; }
    bool Send();
}

public class SMSMessage : IMessage
{
    IList<string> SendToAddress { get; set; }
    string MessageText { get; set; }
    string Subject
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }
 
    string CCToAddress
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }
 
    bool Send()
    {
        // Contact SMS server and send message
    }
}

It would get even worse with BCCToAddress and email attachments.

Applying Interface Segregation Principle

A better way is to put the interface on a diet and have it comply with the Interface Segregation Principle.

public interface IMessage
{
    IList<string> SendTo { get; set; }
    string MessageText { get; set; }
    bool Send();
}
 
public interface IEmailMessage
{
    IList<string> CCTo { get; set; }
    IList<string> BCCTo { get; set; }
    IList<string> AttachementFilePaths { get; set; }
    string Subject { get; set; }
}
 
public class EmailMessage : IMessage, IEmailMessage
{
    IList<string> SendTo { get; set; }
    IList<string> CCTo { get; set; }
    IList<string> BCCTo { get; set; }
    IList<string> AttachementFilePaths { get; set; }
    string Subject { get; set; }
    string MessageText { get; set; }
    
    bool Send()
    {
        // Contact SMTP server and send message
    }
}
 
public class SMSMessage : IMessage
{
    IList<string> SendTo { get; set; }
    string MessageText { get; set; }
 
    bool Send()
    {
        // Contact SMS server and send message
    }
}

So, put your interfaces on a diet. Don’t make them fat. By doing so, you will allow your software to grow and thrive and be lush, green and vibrant. In the next issue, I’ll finish up the discussion of SOLID.

About Software Gardening

Comparing software development to constructing a building says that software is solid and difficult to change. Instead, we should compare software development to gardening as a garden changes all the time. Software Gardening embraces practices and tools that help you create the best possible garden for your software, allowing it to grow and change with less effort. Learn more in What is Software Gardening.

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Craig Berntson works for one of the largest mortgage companies in the US where he specializes in middleware development and helping teams get better. He has spoken at developer events across the US, Canada, and Europe for over 20 years and is a Grape City Community Influencer. Craig is the coauthor of 'Continuous Integration in .NET' available from Manning. He has been a Microsoft MVP since 1996. Craig lives in Salt Lake City, Utah. Email: dnc@craigberntson.com Twitter: @craigber.


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!