BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Programming Concepts

Interface Segregation Principle

The fifth article in the SOLID Principles series describes the Interface Segregation Principle (ISP). The ISP specifies that clients should not be forced to depend upon interfaces that they do not use. Instead, those interfaces should be minimised.

The Principle

Some classes have public interfaces that are not cohesive. They may include several groups of members where each group is used by a different set of client classes. The groups may be entirely separate or there may be overlap between the members used by different clients. Ideally all classes would have cohesive interfaces. Unfortunately, this is not always possible.

The Interface Segregation Principle (ISP) states that clients should not be forced to depend upon interfaces that they do not use. When we have non-cohesive interfaces, the ISP guides us to create multiple, smaller, cohesive interfaces. The original class implements each such interface. Client code can then refer to the class using the smaller interface without knowing that other members exist.

When you apply the ISP, class and their dependencies communicate using tightly-focussed interfaces, minimising dependencies on unused members and reducing coupling accordingly. Smaller interfaces are easier to implement, improving flexibility and the possibility of reuse. As fewer classes share interfaces, the number of changes that are required in response to an interface modification is lowered. This increases robustness.

Example Code

To demonstrate the application of the ISP, we can review some code that violates it and explain how to refactor to comply with the principle. The following code shows the outline of three classes:

public class Contact
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string EmailAddress { get; set; }
    public string Telephone { get; set; }
}

    
public class Emailer
{
    public void SendMessage(Contact contact, string subject, string body)
    {
        // Code to send email, using contact's email address and name
    }
}

    
public class Dialler
{
    public void MakeCall(Contact contact)
    {
        // Code to dial telephone number of contact
    }
}

The Contact class represents a person or business that can be contacted. The class holds the person's name, address, email address and telephone number. The Emailer class sends email messages to contacts. The contact and the subject and body of the email are passed to the parameters. The Dialler class extracts the telephone number from the Contact and calls it using an automatic dialling system.

The example code violates the ISP. The Emailer class is a client of the Contact class. Although it only requires access to the Name and EmailAddress properties, it is aware of other members too. Similarly, the Dialler class uses a single property, "Telephone". However, it has access to the entire Contact interface.

Refactored Code

To refactor the code to comply with the ISP we need to hide unused members from the client classes. We can achieve this by introducing two new interfaces, both implemented by Contact. The IEmailable interface defines properties that hold the name and email address of an object that can receive email. The IDiallable interface includes only a Telephone property, which is enough to allow client classes to call the telephone number of a target object.

The Email class is updated, replacing the Contact dependency with an IEmailable object. Similarly, the Dialler's dependency becomes an IDiallable instance. Both classes now interact with contacts using the smallest possible interface.

With smaller interfaces it is easier to introduce new classes that implement them. To demonstrate, the refactored code includes a new class named "MobileEngineer". This represents engineers that visit customer sites. Engineer has properties for a name, telephone number and vehicle registration. The class implements IDiallable so that the Dialler object can call engineers.

NB: The refactored example code below breaks other SOLID principles in order that the application of the ISP is obvious. Further refactoring of this example is necessary to achieve full compliance.

public interface IEmailable
{
    string Name { get; set; }
    string EmailAddress { get; set; }
}


public interface IDiallable
{
    string Telephone { get; set; }
}


public class Contact : IEmailable, IDiallable
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string EmailAddress { get; set; }
    public string Telephone { get; set; }
}


public class MobileEngineer : IDiallable
{
    public string Name { get; set; }
    public string Vehicle { get; set; }
    public string Telephone { get; set; }
}


public class Emailer
{
    public void SendMessage(IEmailable target, string subject, string body)
    {
        // Code to send email, using target's email address and name
    }
}


public class Dialler
{
    public void MakeCall(IDiallable target)
    {
        // Code to dial telephone number of target
    }
}
20 January 2011