The Interface Segregation Principle states that a class should not depend on methods it does not use.
We’ve arrived at the letter I of SOLID: the Interface Segregation Principle (ISP).
While the previous principle (LSP) dealt with how hierarchies behave, this one focuses on how we define the contracts between our classes.
The formal definition states:
“Clients should not be forced to depend upon interfaces that they do not use.”
Put more simply, it’s better to have small, specific interfaces than one giant interface that does everything.
Giant interfaces are known as “Fat Interfaces” or “God Interfaces”. The problem with these interfaces is that they force the classes that implement them to carry methods they don’t need, generating unnecessary boilerplate code and coupling.
The Classic Example: The Multifunction Machine
The most visual example to understand this is an office setting. Suppose we define an interface for office devices. Wanting to cover everything, we create this:
The Bad Design
public interface IOfficeMachine
{
void Print(Document doc);
void Scan(Document doc);
void SendFax(Document doc);
}
Now the IT team comes and installs a Basic Printer (one of those old ones that can only print). When implementing the interface, we run into a problem:
public class BasicPrinter : IOfficeMachine
{
public void Print(Document doc)
{
// Actual printing logic...
Console.WriteLine("Printing...");
}
public void Scan(Document doc)
{
// What do we do here? I can't scan!
throw new NotImplementedException();
}
public void SendFax(Document doc)
{
// Fax? What century are we in?
throw new NotImplementedException();
}
}
Do you see the problem?
- Dirty Code: The
BasicPrinterclass is full of methods that throw exceptions or do nothing. - LSP Violation: As we saw in the previous article, throwing
NotImplementedExceptionviolates the Liskov Substitution Principle. - Coupling: If we change the signature of the
SendFaxmethod in the interface, we have to recompile theBasicPrinterclass, even though it doesn’t even use the fax!
If you see a class that implements an interface and leaves methods empty ({ }) or throws “not implemented” exceptions, it’s a warning sign that the interface is too large.
Segregating the Interface
To comply with the ISP, we must segregate (divide) the large interface into smaller, more cohesive ones. This involves grouping methods by roles.
public interface IPrinter
{
void Print(Document doc);
}
public interface IScanner
{
void Scan(Document doc);
}
public interface IFax
{
void SendFax(Document doc);
}
Now, our Basic Printer only signs the contract it can fulfill.
// ✅ GOOD DESIGN: Only implements what it uses
public class BasicPrinter : IPrinter
{
public void Print(Document doc)
{
Console.WriteLine("Printing...");
}
}
What if we have a state-of-the-art multifunction photocopier? Then we implement multiple interfaces.
public class Photocopier : IPrinter, IScanner, IFax
{
public void Print(Document doc) { /* ... */ }
public void Scan(Document doc) { /* ... */ }
public void SendFax(Document doc) { /* ... */ }
}
This is much more flexible. The photocopier is a printer, and it is a scanner. But the basic printer is not forced to be a scanner.
Benefits of ISP
Applying this principle yields several advantages:
- Decoupling: Changes in one interface (e.g.,
IFax) do not affect classes that don’t use it (BasicPrinter). - Readability: When you see a class implements
IPrinter, you know exactly what to expect. You don’t have to guess which methods work and which don’t. - Reusability: It’s much easier to reuse small interfaces (“Role Interfaces”) in different parts of the application.
In languages like C# or Java, a class can inherit from only one parent class, but it can implement as many interfaces as needed.
When to Apply ISP?
As with SRP (Single Responsibility Principle), don’t go overboard and create an interface for every single method (an interface with one method is usually overkill, unless it’s a functional pattern).
Apply ISP when:
- You have an interface with many methods and notice that classes only use a subset of them.
- You have clients (other classes) that use that interface but only call one or two methods.