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.
Top comments (0)