Interface Segregation Principle
The I in SOLID: no class should be forced to implement methods it does not use. Split big interfaces into small ones that each describe one role.
On this page · 5 min · Intermediate
On this page
- In simple words
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Intermediate
- Updated
- Oct 28, 2026
Related tutorials
In simple words#
A remote with 80 buttons when you only need 5 is annoying. Prefer several small, focused interfaces over one giant one.
- ISP
- Don’t force classes to implement unused methods.
- Fat interface
- Too many unrelated members.
- Role interfaces
- Small interfaces per capability.
Learning objectives#
- State the Interface Segregation Principle
- Spot fat interfaces
- Split an interface into role interfaces
- Combine small interfaces in one class
Explanation#
A fat interface such as IMachine { Print(); Scan(); Fax(); } forces a basic printer to implement Scan and Fax — usually by throwing “not supported”.
Those fake implementations are traps: callers think every IMachine can scan, and find out at runtime that it cannot (an LSP violation too).
ISP says: split by role. IPrinter { Print(); }, IScanner { Scan(); }, IFax { Fax(); }. A class implements only the roles it really has.
A multi-function office machine simply implements several: class OfficeMachine : IPrinter, IScanner.
Clients depend only on what they use. A PrintQueue needs IPrinter, nothing more — so it works with every printer.
Syntax#
// ✗ Fat interface
interface IMachine { void Print(string doc); void Scan(string doc); void Fax(string doc); }
// ✓ Role interfaces
interface IPrinter { void Print(string doc); }
interface IScanner { void Scan(string doc); }
class BasicPrinter : IPrinter { public void Print(string doc) { } }
class OfficeMachine : IPrinter, IScanner
{
public void Print(string doc) { }
public void Scan(string doc) { }
}
Code example: Printers that do not pretend to scan#
// ✗ Fat contract: every device must print, scan and fax
class OldPrinter {
print(doc) { return `Printing ${doc}`; }
scan() { throw new Error('Not supported'); }
fax() { throw new Error('Not supported'); }
}
// ✓ Small role-based capabilities
const canPrint = d => typeof d.print === 'function';
const canScan = d => typeof d.scan === 'function';
class BasicPrinter {
print(doc) { return `Printing ${doc}`; }
}
class OfficeMachine {
print(doc) { return `Printing ${doc}`; }
scan(doc) { return `Scanning ${doc}`; }
}
const devices = [new BasicPrinter(), new OfficeMachine()];
console.log(devices.filter(canScan).map(d => d.scan('passport.pdf')));
console.log(devices.filter(canPrint).length);
try { new OldPrinter().scan('x'); } catch (e) { console.log('OldPrinter:', e.message); }
[ 'Scanning passport.pdf' ] 2 OldPrinter: Not supported
Step-by-step explanation#
- OldPrinter claims to scan and fax but throws — a promise it cannot keep.
- BasicPrinter implements only printing; OfficeMachine implements printing and scanning.
- Code that needs scanning asks for the scanning capability and gets only devices that really can.
- Both devices still count as printers, so the print queue works with either.
Real-world use case#
A data layer splits IReadRepository<T> (Get, List) from IWriteRepository<T> (Add, Update, Delete). Reporting services depend only on the read interface and cannot accidentally modify data.
Common mistakes#
Mirroring every public method of a class in a single interface recreates the fat-interface problem.
Ten one-method interfaces that are always used together add noise. Group by what clients actually need.
Best practices#
- Design interfaces from the client’s point of view
- Name interfaces after roles (IPrinter, IScanner)
- Let one class implement several small interfaces
- Watch for implementations that throw NotSupportedException
Remember this#
Many small interfaces > one fat interface.
Never force a class to implement what it cannot do.
Clients depend only on what they use.
Practice exercise#
Your task: A Worker contract has work() and eat(). A Robot can work but not eat. Split it into Workable and Eatable roles, implement Human (both) and Robot (work only), and run a shift that calls work() on everyone who can work.
Need a hint?
Show solution
class Human { work() { return 'human working'; } eat() { return 'human eating'; } }
class Robot { work() { return 'robot working'; } }
const staff = [new Human(), new Robot()];
console.log(staff.filter(s => typeof s.work === 'function').map(s => s.work()));
// [ 'human working', 'robot working' ]
console.log(staff.filter(s => typeof s.eat === 'function').length); // 1
Mini quiz#
What does ISP recommend?
Want more? Practise all OOP Concepts MCQs · OOP Concepts interview questions
My notes#
Saved in this browser.