Interface Segregation Principle in C#: Build Interfaces People Actually Want to Use
Donât Make Me Implement That
Weâve all seen itâa class thatâs forced to implement methods it doesnât care about just because âthe interface says so.â
Thatâs what the Interface Segregation Principle (ISP) is here to fix.
Clients should not be forced to depend on interfaces they do not use. â Robert C. Martin
Letâs break that down with real-world C# examples and some common sense.
The Problem: One Interface to Rule Them All
Imagine weâre building a system for handling different types of documents. Hereâs a common interface you might start with:
public interface IDocumentProcessor
{
void Print();
void Fax();
void Scan();
}
Looks fine, until we create a class for a DigitalDocument:
public class DigitalDocument : IDocumentProcessor
{
public void Print()
{
throw new NotSupportedException("Cannot print digital-only documents.");
}
public void Fax()
{
throw new NotSupportedException("Fax not supported.");
}
public void Scan()
{
// Scan logic (maybe)
}
}
Weâre already violating the Interface Segregation Principle.
The Smell: When throw
Shows Up
When your implementation starts throwing NotSupportedException
, itâs a clear sign the interface is doing too much.
The class is forced to depend on behaviors it doesnât need, just to satisfy the compiler. And now, other parts of the system have to be defensive and check whatâs really supported.
â The Fix: Split the Interface
Letâs break things down into focused, role-specific interfaces:
public interface IPrintable
{
void Print();
}
public interface IFaxable
{
void Fax();
}
public interface IScannable
{
void Scan();
}
Now each class only implements what it actually supports:
public class DigitalDocument : IScannable
{
public void Scan()
{
Console.WriteLine("Scanning digital document...");
}
}
public class PhysicalDocument : IPrintable, IFaxable, IScannable
{
public void Print() => Console.WriteLine("Printing...");
public void Fax() => Console.WriteLine("Faxing...");
public void Scan() => Console.WriteLine("Scanning...");
}
Cleaner, clearer, and no wasted methods.
đŠ A More Realistic Use Case: Invoicing
Letâs bring it back to invoices. You might start with:
public interface IInvoiceHandler
{
void Save();
void Email();
void Print();
}
But what if some invoices are draft-only, or internal, and donât need to be emailed or printed?
Split the interface:
public interface IInvoiceSaver
{
void Save();
}
public interface IInvoiceMailer
{
void Email();
}
public interface IInvoicePrinter
{
void Print();
}
Now your classes arenât cluttered with unused methods or fake throw
logic. Each one does exactly what it needs toâand nothing more.
đ§Ș A Quick Check
Ask yourself:
- Is this class implementing methods it doesnât actually use?
- Are you seeing ânot supportedâ or empty implementations?
- Could this interface be broken into smaller, more focused pieces?
If yes, youâre likely looking at an ISP violation.
đŹ Final Thoughts
The Interface Segregation Principle is all about respect. Respect for your classes, your future teammates, and your own sanity.
When interfaces are clean and focused, your code becomes easier to understand, test, and extend. No more placeholder methods. No more fake support for features. Just clean, intentional design.
So next time youâre tempted to add âjust one more methodâ to that interface, pauseâand maybe make a new one instead.
A good interface is like a good conversationâshort, relevant, and free of awkward silence.