Refactoring to SOLID - Part 5: The Interface Segregation Principle

by Larry Spencer Saturday, May 12, 2012 12:05 PM

We have been considering what it takes to refactor an old-style ASP.NET application to one that obeys the principles of SOLID development. If you're just joining us, you might want to read Part 1, which explains what each letter of the SOLID acronym stands for.

We have now progressed to the letter 'I', for Interface Segregation Principle. To explain and justify this principle, I must give you a peek at what's behind the letter 'D' -- Dependency Inversion. 

The principle of Dependency Inversion is that callers should depend on abstractions of what they call, not concrete classes. This allows you to swap out different implementations of the abstraction without breaking anything.

As a C# or VB developer, you know that one form of abstraction is the interface. (Another form is the abstract class.) You are no doubt familiar with making a class inherit from an interface. The Interface Segregation Principle says that you should consider splitting that interface into several, and inheriting from all of them. You would segregate based on how the various interfaces are used, and/or who uses them.

The best example I have for you is not from our Refactoring to SOLID project, but from a situation at my day job. I have a class, which I'll call MyAuthorizer, that manages authorization chores for our applications. Here's a boiled-down version.

 

public enum RestrictedOperation
{
    DoThis,
    DoThat,
    DoTheOtherThing,
}

public class MyAuthorizer
{
    public bool IsAllowed(RestrictedOperation operation)
    {
        // Query authorization database, etc.
    }

    public void AssignToGroup(int userId, int groupId)
    {
        // Assign the user to the group.
    }

    public void RemoveFromGroup(int userId, int GroupId)
    {
        // Remove the user from the group.
    }

    void Allow(int groupId, RestrictedOperation operation)
    {
        // Give members of the group the ability to do the operation.
    }

    public void Prohibit(int groupId, RestrictedOperation operation)
    {
        // Ensure members of the group cannot do the operation.
    }
}

 

This class obeys the Single Responsibility Principle but that doesn't mean it's used in only one way. Most consumers only want to answer the question, "Am I allowed to do such-and-such?" An administrator client, which is much more rare, might want to assign authorizations to users, or put users in groups.

Thus, it is sensible to make the class inherit from two interfaces:

 

// Interface for a typical user who just makes authorization queries.
public interface IAuthorizerUser
{
    void Allow(int groupId, RestrictedOperation operation);
}

// Interface for an administrator.
public interface IAuthorizerAdmin
{
    void Allow(int groupId, RestrictedOperation operation);
    void Prohibit(int groupId, RestrictedOperation operation);
    void AssignToGroup(int userId, int groupId);
    void RemoveFromGroup(int userId, int GroupId);
}

 

The class then inherits from both interfaces:

 

public class MyAuthorizer 
    : IAuthorizerUser, IAuthorizerAdmin
{
    // etc.
}

 

Why is this a good idea? I can change any aspect of the administration portion, assured that I can't possibly mess up the vast majority of my consumers, who only use the IAuthorizerUser interface.

When we deal with dependency injection in the next post, you'll see some powerful ways to use interfaces and you'll gain an even greater appreciation of the Interface Segregation Principle.

Tags: ,

All | Dependency Injection | Talks

Pingbacks and trackbacks (1)+

Add comment

About the Author

Larry Spencer

Larry Spencer develops software with the Microsoft .NET Framework for ScerIS, a document-management company in Sudbury, MA.