ISP - The Interface Segregation Principle

4 min read
By Thiago Souza
SOLIDISPSoftware DesignC#

The ISP (Interface Segregation Principle) was formulated and used for the first time by Robert C. Martin (the famous Uncle Bob), while he was conducting a consultancy for Xerox.

This principle, in my opinion, is the simplest of the 5 SOLID Principles and, at the same time, the most neglected among software developers and architects. However, this depends a lot on the programming language used.

Dynamically typed languages, such as Python for example, don't force any type of implementation. This happens due to the fact that declarations are inferred at runtime.

Definition of ISP

I haven't brought any beautiful phrase written (or said) by some software architecture star, but we can define and state that:

Interface segregation should be performed whenever a type/class is being forced to implement an attribute and/or behavior that makes no sense and/or has no purpose.

Let's go to the examples; if something is not clear, the application section consolidates the idea.

Violation of ISP

We can imagine a simple scenario where we have an IPrinter interface that has two methods: Print and Scan. Our program has two classes that implement this interface, called AdvancedPrinter and BasicPrinter.

The AdvancedPrinter class can print and scan documents, and therefore implements both methods. The BasicPrinter class can only print documents, so it implements only the Print method and throws an exception if the Scan method is triggered. Here's the code with this example:

public record Document(string Name, string Content);

public interface IPrinter
{
    void Print(Document document);

    Document Scan();
}

public class AdvancedPrinter : IPrinter
{
    public void Print(Document) 
    {
        // Logic to print the document.
    }

    public Document Scan()
    {
        // Logic to scan a document.
    }
}

public class BasicPrinter : IPrinter
{
    public void Print(Document) 
    {
        // Logic to print the document.
    }

    public Document Scan()
    {
        throw new NotImplementedException(
            "This printer cannot scan documents.");
    }
}
Sad face

Situations like this are more common than they seem and, in general, indicate opportunities to improve cohesion and reduce coupling.

It's part of the team's evolution to identify these cases and replace them with a design more adherent to the domain.

But if you still have doubts about what should be done in this case, don't worry! This article was written with great care to help people like you! People who have a sparkle in their eyes and an interest in building quality code.

Application of ISP

To be in compliance with the Interface Segregation Principle and solve this problem is really very simple!

Following an approach similar to that used in the article about LSP, we need to separate behaviors into distinct interfaces and implement them only when they are really necessary.

Thus, we can correct the example presented earlier as follows:

public record Document(string Name, string Content);

public interface IPrinter // Has only the print method.
{
    void Print(Document document);
}

public interface IScanner // Has only the scan method.
{
    Document Scan();
}

public class AdvancedPrinter 
    : IPrinter, IScanner // Implements both interfaces.
{
    public void Print(Document) 
    {
        // Logic to print the document.
    }

    public Document Scan()
    {
        // Logic to scan a document.
    }
}

public class BasicPrinter 
    : IPrinter // Implements only the interface with the print method.
{
    public void Print(Document) 
    {
        // Logic to print the document.
    }
}

The approach is straightforward: throwing exceptions or creating empty methods just to fulfill an interface is an antipattern. Prioritize coherence, durability, and longevity of the code.


Thanks for reading. To deepen: Clean Architecture — A Craftsman's Guide to Software Structure and Design (Robert C. Martin, 2019).