Saturday, September 23, 2017

SOLID - 4. The Interface Segregation Principle

Next up in the series about the five SOLID principles, let's take a look at the Interface Segregation Principle (ISP). 


You can read up on my other articles on SOLID here:


The Interface Segregation Principle


The Interface Segregation Principle addresses the cohesion of interfaces and says that clients should not be forced to rely on methods they do not use. To have a cohesive and reusable class, we must give it a single responsibility. But sometimes, even this single responsibility can be broken into even smaller responsibilities, making your interface more user friendly.

Let's see how to better clarify the concept with an example. Suppose we had defined the interface ScrumTeamMember as follows:

public interface IScrumTeamMember
{
    void PrioritizeBacklog();
    void ShieldTeam();
    void DevelopFeatures();
}

And then we add the Developer, ScrumMaster, and ProductOwner classes implementing the ScrumTeamMember interface:

public class Developer : IScrumTeamMember
{
    public void PrioritizeBacklog() {}
    public void ShieldTeam() {}
    public void DevelopFeatures()
    {
        Console.Writeline("Developing new features");
    }
}

public class ScrumMaster : IScrumTeamMember
{
    public void PrioritizeBacklog() {}
    public void ShieldTeam()
    {
        Console.Writeline("Shield Scrum Development Team");
    }
    public void DevelopFeatures() {}
}

public class ProductOwner : IScrumTeamMember
{
    public void PrioritizeBacklog()
    {
        Console.Writeline("Prioritize backlog items");
    }
    public void ShieldTeam() {}
    public void DevelopFeatures() {}
}

When we create a generic interface, we end up causing an implementation, in the Developer case, not to use certain interface methods. This is what happens with the PrioritizeBacklog and ShieldTeam methods, which do nothing because they are not attributions of a Developer, but of ProductOwner and ScrumMaster, respectively.

Problems

Suppose that some change is required in the ShieldTeam method, which now needs to receive some parameters. Thus, we are required to change all implementations of ScrumTeamMember (Developer, ScrumMaster and ProductOwner) because of a change that should only affect the ScrumMaster class.

In addition, client-side classes that depend on ScrumTeamMember will have to be recompiled and if they are in several components they will have to be redistributed aswell. Sometimes unnecessarily, because they did not even use the ShieldTeam method.

Another problem is that implementing useless methods (degenerates) can lead to the violation of the Liskov Substitution Principle, since someone using ScrumTeamMember could assume the following:

foreach (var member in scrumTeamMembers)
{
    member.DevelopFeatures();
}

However, we know that only a Developer performs the above behavior. If the list also had objects of type ScrumMaster or ProductOwner, these objects would not be doing anything, or worse, could trigger some exception, if the implementation of them did so.

Resolving the ISP violation

The solution to the above example is to create more specific interfaces so that each client class depends only on what it actually needs. For example:

public interface IScrumMasterFunction
{
    void ShieldTeam();
}

public class ScrumMaster : IScrumMasterFunction
{
    public void ShieldTeam()
    {
        Console.Writeline("Shield the Scrum Development Team");
    }
}

With the above change, the ScrumMaster concrete class no longer needs to implement unnecessary methods, and other classes that depended on ScrumTeamMember only to use ShieldTeam may now depend on the ScrumMasterFunction interface.

The same idea can be applied to the specific functions of Developer and ProductOwner, so that all the ScrumTeamMember clients can now depend specifically on the interfaces they use.

Conclusion

The Interface Segregation Principle alerts us of dependencies on "fat interfaces," forcing concrete classes to implement unnecessary methods and causing a large coupling between all clients. By using more specific interfaces, we break this coupling between client classes.

In addition, Interface Segregation Principle helps us increase the granularity of our objects, increasing the cohesion of their interfaces and drastically reducing coupling. This improves the maintenance of our code, since simpler interfaces are easier to understand and implement.