Summary
This article explores the Interface Segregation Principle (ISP)—clients should not be forced to depend on interfaces they don't use. You'll learn how to design focused, cohesive interfaces instead of bloated "kitchen sink" interfaces. Through practical C# examples, we'll refactor fat interfaces into lean, purpose-specific contracts that make code more maintainable, testable, and flexible.
Prerequisites
Strong understanding of C# interfaces
Familiarity with implementing and consuming interfaces
Knowledge of previous SOLID principles helpful
Basic understanding of dependency injection
The Interface Segregation Principle states: No client should be forced to depend on methods it does not use.
In simpler terms: many small, focused interfaces are better than one large, general-purpose interface. Let's see why.
The Worker Problem
You're building a workforce management system. You create an interface for all workers:
public interface IWorker
{
void Work();
void Eat();
void Sleep();
void TakeBreak();
}
public class HumanWorker : IWorker
{
public void Work()
{
Console.WriteLine("Human working...");
}
public void Eat()
{
Console.WriteLine("Human eating lunch...");
}
public void Sleep()
{
Console.WriteLine("Human sleeping...");
}
public void TakeBreak()
{
Console.WriteLine("Human taking a break...");
}
}
Works fine. Then you add robots:
public class Robot : IWorker
{
public void Work()
{
Console.WriteLine("Robot working 24/7...");
}
public void Eat()
{
throw new NotImplementedException("Robots don't eat");
}
public void Sleep()
{
throw new NotImplementedException("Robots don't sleep");
}
public void TakeBreak()
{
throw new NotImplementedException("Robots don't take breaks");
}
}
This violates ISP. Robot is forced to implement methods it doesn't use. Worse, these methods throw exceptions or do nothing.
Refactoring with ISP
Let's segregate the interface:
public interface IWorkable
{
void Work();
}
public interface IEatable
{
void Eat();
}
public interface ISleepable
{
void Sleep();
}
public interface IBreakable
{
void TakeBreak();
}
public class HumanWorker : IWorkable, IEatable, ISleepable, IBreakable
{
public void Work()
{
Console.WriteLine("Human working...");
}
public void Eat()
{
Console.WriteLine("Human eating...");
}
public void Sleep()
{
Console.WriteLine("Human sleeping...");
}
public void TakeBreak()
{
Console.WriteLine("Human on break...");
}
}
public class Robot : IWorkable
{
public void Work()
{
Console.WriteLine("Robot working continuously...");
}
}
Now Robot only implements what it needs. No dummy implementations, no exceptions.
The Printer Problem
Here's another common violation. You're managing office devices:
public interface IMultiFunctionDevice
{
void Print(Document document);
void Scan(Document document);
void Fax(Document document);
void Copy(Document document);
void Email(Document document);
}
public class AllInOnePrinter : IMultiFunctionDevice
{
public void Print(Document document)
{
Console.WriteLine("Printing...");
}
public void Scan(Document document)
{
Console.WriteLine("Scanning...");
}
public void Fax(Document document)
{
Console.WriteLine("Faxing...");
}
public void Copy(Document document)
{
Console.WriteLine("Copying...");
}
public void Email(Document document)
{
Console.WriteLine("Emailing...");
}
}
Fine for all-in-one devices. But what about a simple printer?
public class SimplePrinter : IMultiFunctionDevice
{
public void Print(Document document)
{
Console.WriteLine("Printing...");
}
public void Scan(Document document)
{
throw new NotSupportedException("This printer cannot scan");
}
public void Fax(Document document)
{
throw new NotSupportedException("This printer cannot fax");
}
public void Copy(Document document)
{
throw new NotSupportedException("This printer cannot copy");
}
public void Email(Document document)
{
throw new NotSupportedException("This printer cannot email");
}
}
SimplePrinter is forced to implement methods it doesn't support. ISP violation.
Segregated Interfaces
public interface IPrinter
{
void Print(Document document);
}
public interface IScanner
{
void Scan(Document document);
}
public interface IFax
{
void Fax(Document document);
}
public interface ICopier
{
void Copy(Document document);
}
public interface IEmailer
{
void Email(Document document);
}
public class SimplePrinter : IPrinter
{
public void Print(Document document)
{
Console.WriteLine("Printing...");
}
}
public class AllInOnePrinter : IPrinter, IScanner, IFax, ICopier, IEmailer
{
public void Print(Document document)
{
Console.WriteLine("Printing...");
}
public void Scan(Document document)
{
Console.WriteLine("Scanning...");
}
public void Fax(Document document)
{
Console.WriteLine("Faxing...");
}
public void Copy(Document document)
{
Console.WriteLine("Copying...");
}
public void Email(Document document)
{
Console.WriteLine("Emailing...");
}
}
public class PrinterScanner : IPrinter, IScanner
{
public void Print(Document document)
{
Console.WriteLine("Printing...");
}
public void Scan(Document document)
{
Console.WriteLine("Scanning...");
}
}
Now each device implements only the interfaces it supports. Clean, focused, and no exceptions.
The Repository Anti-Pattern
Consider a generic repository:
public interface IRepository
{
T GetById(int id);
IEnumerable GetAll();
void Add(T entity);
void Update(T entity);
void Delete(int id);
void BulkInsert(IEnumerable entities);
void BulkDelete(IEnumerable ids);
IEnumerable Search(string query);
void ExecuteStoredProcedure(string procedureName, params object[] parameters);
}
Looks comprehensive. But what if you have a read-only view?
public class ReportRepository : IRepository
{
public Report GetById(int id)
{
// Implementation
}
public IEnumerable GetAll()
{
// Implementation
}
public IEnumerable Search(string query)
{
// Implementation
}
// Reports are read-only, so these shouldn't exist
public void Add(Report entity)
{
throw new NotSupportedException();
}
public void Update(Report entity)
{
throw new NotSupportedException();
}
public void Delete(int id)
{
throw new NotSupportedException();
}
public void BulkInsert(IEnumerable entities)
{
throw new NotSupportedException();
}
public void BulkDelete(IEnumerable ids)
{
throw new NotSupportedException();
}
public void ExecuteStoredProcedure(string procedureName, params object[] parameters)
{
throw new NotSupportedException();
}
}

Join the conversation! Your thoughts help the community grow.