When building applications, we often create classes or interfaces that contain many different methods.
At first, this may seem convenient because everything is available in one place.
But sometimes a class only needs a few of those methods. Forcing it to depend on methods it does not use can make the code unnecessarily complicated.
The Interface Segregation Principle (ISP) helps solve this problem.
What is Interface Segregation Principle?
The idea is simple:
A class should not be forced to depend on methods it does not need.
Instead of creating one large interface with many unrelated methods, we should create smaller and more focused interfaces.
Why This Matters
When a class depends on methods it does not need:
- The code becomes harder to understand
- Classes may contain unnecessary methods
- We may need empty or unsupported implementations
- Changes can affect unrelated parts of the system
Instead, we should keep our interfaces small and focused.
Bad Example (One Large Interface)
Let's imagine we are building a notification system.
We create one large class that handles different types of notifications:
class NotificationService {
sendEmail(message) {
console.log(`Sending email: ${message}`);
}
sendSMS(message) {
console.log(`Sending SMS: ${message}`);
}
sendPushNotification(message) {
console.log(`Sending push notification: ${message}`);
}
}
Now imagine we create an email-only notification class:
class EmailNotification extends NotificationService {
sendEmail(message) {
console.log(`Email: ${message}`);
}
sendSMS(message) {
throw new Error("SMS is not supported");
}
sendPushNotification(message) {
throw new Error("Push notifications are not supported");
}
}
Problem here:
EmailNotificationonly needssendEmail()- It is still forced to implement SMS and push notification methods
- Some methods are not actually useful for this class
This means our interface or contract is too large.
Good Example (Smaller Interfaces)
Instead of forcing every notification type to support every operation, we can separate the different behaviors.
class EmailNotifier {
send(message) {
console.log(`Sending email: ${message}`);
}
}
class SMSNotifier {
send(message) {
console.log(`Sending SMS: ${message}`);
}
class PushNotifier {
send(message) {
console.log(`Sending push notification: ${message}`);
}
}
Now each class only contains the behavior it actually needs.
We can use them like this:
function sendNotification(notifier, message) {
notifier.send(message);
}
const emailNotifier = new EmailNotifier();
const smsNotifier = new SMSNotifier();
sendNotification(emailNotifier, "Welcome!");
sendNotification(smsNotifier, "Your verification code is 1234");
Why this is better:
- Each class has a focused purpose
- Classes do not need unnecessary methods
- The code is easier to understand
- New notification types can be added easily
Adding a New Notification Type
For example, if we want to add WhatsApp notifications, we can simply create a new class:
class WhatsAppNotifier {
send(message) {
console.log(`Sending WhatsApp message: ${message}`);
}
}
We don't need to modify the existing notification classes.
We can use the new notifier in the same way:
const whatsappNotifier = new WhatsAppNotifier();
sendNotification(
whatsappNotifier,
"Your order has been shipped!"
);
The calling code only expects the object to provide the send() behavior.
Real-Life Analogy
Think about a restaurant system.
A customer may need features like:
- View the menu
- Place an order
- Make a payment
But a delivery driver may only need:
- View assigned orders
- Update delivery status
It would not make sense to force the delivery driver to use every feature available to the customer.
Each role should get only the features it actually needs.
The same idea applies to software interfaces.
Key Idea to Remember
- Keep interfaces small and focused
- Don't force classes to implement unnecessary methods
- Separate unrelated behaviors
- Make classes depend only on what they actually need
Conclusion
When designing software, it can be tempting to create one large interface that handles everything.
But as the application grows, this can make classes depend on methods they don't actually need.
By following the Interface Segregation Principle, we can create smaller and more focused interfaces.
This makes our code:
- Easier to understand
- Easier to maintain
- Less dependent on unnecessary functionality
- Easier to extend
The core idea is simple:
Don't force a class to depend on something it doesn't need.
Summary
- ISP stands for Interface Segregation Principle
- It is the fourth principle of SOLID
- Classes should not be forced to depend on unnecessary methods
- Large interfaces can be divided into smaller, focused interfaces
- Smaller interfaces make code easier to maintain and extend