Interface Segregation Principle

This is the fourth post in covering the SOLID development principles. In this post, we are going to be covering the Interface Segregation Principle (ISP). This principle continues to build on was covered previously. Think about how we’ve been using interfaces and abstract classes previously. In this principle, when we talk about interfaces, we mean anything from a base class to interfaces as implemented in C#. As always, this principle applies to more than just the C# language, but I will be giving examples in C#. If you haven’t caught on yet, these principles work with one another and are there as guidelines to help us write more flexible and maintainable code.

Interface Segregation Principle in Action

The Interface Segregation Principle states “Clients should not be forced to depend on methods that they do not use.”, and no this is not a web development related client. The client, in this case, is the class that inherits from our interface or base class. This principle is also very closely related to the Single Responsibility Principle which you can read about in this post.

Let’s build upon our previous examples of a Customer Class, and let’s say we have multiple types of customers.

Imagine that we have a few products which we offer to our customers. The first product we’d like to offer is T-Shirts. So we’d start with a generic interface.

public interface IProduct
{
    int Id { get; set; }
    int Quantity { get; set; }
    string Size { get; set; }
    bool LongSleeves { get; set; }
}

And then we’d have a TShirt class which will inherit from the IProduct interface.

public class TShirt : IProduct
{
    public int Id { get; set; }
    public int Quantity { get; set; }
    public string Size { get; set; }
    public bool LongSleeves { get; set; }
}

This should all make sense. Now, what if we’re going to offer something like Pants? We’d need to modify our IProduct interface to include something like waist size, or something like the material type. This doesn’t seem to matter to a T-Shirt. We’d end up with a class that is going to throw NotImplementedException as there is no such thing as Long Sleeved pants, and size is usually measured by the attribute of the waistline. Another aspect to consider is that we are in violation of the Liskov Substitution Principle, as that our new Pants class is going to have some additional properties that are not coming from the abstract class that Pants inherits from.

public class Pants : IProduct
{
    public int Id { get; set; }
    public double Weight { get; set; }
    public int Quantity { get; set; }
    public int WaistSize { get; set; }
    public string Material { get; set; }
    public string Size { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
    public bool LongSleeves { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
}

So what is the solution you might be asking? The definite code smell in this example is that we have properties (you could also have methods) that throw the new NotImplementedException(). Also, the properties are not necessarily making sense. So as a solution we create additional interfaces. In C# classes can have only one base class, but the way around this is by using multiple interfaces.

Rewrite

So let’s rewrite these models to use the Interface Segregation Principle.

public interface IProduct
{
    int Id { get; set; }
    int Quantity { get; set; }

}

public interface IShirt
{
     string Size { get; set; }
     bool LongSleeves { get; set; }
}

public interface IPants
{
    int WaistSize { get; set; }
    string Material { get; set; }
}
public class TShirt : IProduct , IShirt
{
    public int Id { get; set; }
    public int Quantity { get; set; }
    public string Size { get; set; }
    public bool LongSleeves { get; set; }
}


public class Pants : IProduct, IPants
{
    public int Id { get; set; }
    public double Weight { get; set; }
    public int Quantity { get; set; }
    public int WaistSize { get; set; }
    public string Material { get; set; }

}

Hopefully, this now makes sense. I find that often times I need to make a little bit of a mess and then clean up to find a clear solution. This example is pretty simple and should be as it is merely an example. This principle once again interweaves closely with the other SOLID principles and I hope is a good heuristic for you and writing cleaner and more maintainable code.

Next On FoC

Dependency Inversion Principle

Dependency Inversion Principle
Previously On FoC

Liskov Substitution Principle

Liskov Substitution Principle