The Interface segregation principle
24th Aug 2020 by Aneesh Mistry

Key Takeaways
• Understand the benefit of abstraction that interfaces bring to design.
• Review the problem of grouping multiple behaviours into a single abstraction.
• Leverage interface segregation to control the behaviours a class must implement from a combination of interfaces.

Interface segregation

Interfaces allow us to achieve an abstraction of behaviours to be implemented by other classes. The abstraction of interfaces enables us to define the behaviours a class must have when they implement an interface 'contract'.

Interfaces have been prevalent throughout the SOLID principles, most notably used with the open-closed principle here.

One of the differences between abstraction and inheritance is that a class can implement multiple interfaces, thus implementing behaviours from multiple classes, however a class can only ever extend a single class.
The interface segregation principle (ISP) encourages well-defined and meaningful interfaces where the behaviours implemented by the class do not pollute its well-defined responsibility.

Much like the Liskov substitution principle, where we aim to inherit classes with the appropriate behaviours, the ISP aims to achieve a similar separation of behaviours for each implementation of an interface.


The contract of interface implementation

The abstract contract of an interface requires the implementing class to take on each behaviour within the interface, and not just selected behaviours. When we design an interface, we must therefore be considerate to the methods we are assigning together as they form the responsibility for the implementing class.

The case for interface segregation arises when classes implement an interface with methods that are not required by the class. As a result, the class obtains excessive behaviours that do not conform to the responsibility of the class. You may think "why not just assign the behaviours without the interface?". This approach will not leverage the benefits of abstraction and can create further issues down the line as the application grows. The Strategy pattern can be valuable to use within our design, therefore we will aim to maintain the use of interfaces while paying consideration to the obstacle of implementing unwanted behaviours.


Interface segregation in action

The below code example demonstrates an interface used for using the DAO design pattern for interacting with a database. The DatabaseOps interface is used for reading and writing to the database and enables the implementations to make both types of requests.

public interface DatabaseOps{

    void testConnection();
    void read();
    void write();
}

There may be many implementations of the DatabaseOps interface, however the implementation of a class to only complete one of the behaviours is not possible. An issue may arise where we would only like to designate the read behaviour to a class, and not the write behaviour as well. The below method illustrates the use of the Strategy pattern where the DatabaseOps interface is a required interface implementation to make calls to the database:

public void makeDatabaseTransaction(DatabaseOps operation){
    operation.read();
}


Segregating the interfaces

We are able to achieve interface segregation by dividing the responsibility of the DatabaseOps interface into two further interfaces that extend from the DatabaseOps interface. The DatabaseOps interface is now split into two further interfaces, ReadDatabaseOps and WriteDatabaseOps:

public interface DatabaseOps{
    void testConnection();
}
public interface ReadDatabaseOps extends DatabaseOps{
    void read();
}
public interface WriteDatabaseOps extends DatabaseOps{
    void write();
}

The image below illustrates the change of the interface design.

Interface

Interface segregation

The change in interface design means the behaviours of reading and writing are separated, however they can both be contracted to a class that implements both the ReadDatabaseOps and WriteDatabaseOps interfaces.


When to use interface segregation

The interface segregation principle should be considered whenever we create an interface. Questions we may want to ask ourselves when designing an interface include: "do we these behaviours to be grouped together?" or "will these behaviours be required within a class on their own?".

We can use the extension of interfaces to design single-purpose, but related abstract contracts that prevent classes from defining behaviours they do not require.


Conclusion

The interface segregation principle extends upon previous SOLID principles including the single responsibility principle and the liskov substitution principle. When we define interfaces, the implementations of the class are asserted into implementing the behaviours of the interface together. The interface segregation principle aims to break-down the abstract behaviours of interfaces into smaller pieces that can prevent unwanted behaviours being implemented for a class.

The interface segregation principle leverages the inheritance of interfaces to break-down and separate behaviours, while class design enables a class to implement multiple interfaces to loosely regroup the behaviours that are required.

Series link: Go to the next SOLID principle: Dependency Inversion Principle


Share this post