This principle was developed by Robert C. Martin while working at Xerox as a consultant. Martin was in charge of developing a new printing management system that could carry out tasks concurrently, which was new by the time.
While developing this new printing system, Martin noticed that small changes in the design caused large-scale deployments. In some same cases caused deployments on modules out of the scope of the change. The problem was mainly caused by a large fat interface used throughout all the modules of the system.
The solution given by Robert C. Martin is what we now know as Interface Segregation Principle (ISP). Which is defined as follows:
Clients should not be forced to depend upon interfaces that they do not use.
This principle is simple and clear, classes should not be forced to implement methods that they do not use. The main goal, just like the Single Responsibility Principle, is to limit the scope of future changes.
Typical Violation of the Principle
Let's suppose that you are the person in charge of developing a new printing system for a new brand of all-in-one printers. This is a brand new system, so you have to design it in a way that you can add new printers to the brand without changing the whole system.
The new printers allow the following capabilities:
- Printing
- Scanning
- Faxing
You define the AllInOneLXPrinter interface to model the basic tasks that a new printer can carry out.
public interface AllInOneLXPrinter {
public boolean print();
public boolean scan();
public boolean fax();
}
You define your first printer class, as follows.
public class LX8578Printer implements AllInOneLXPrinter {
@Override
public boolean print() {
//Logic for printing
return true;
}
@Override
public boolean scan() {
//Logic for scanning
return true;
}
@Override
public boolean fax() {
//Logic for faxing
return true;
}
}
Please take into account that any of these classes have a real-world implementation. They are just used to simplify the example.
The new printing system is a success commercially and technically, as well as the new brand of printers. After the release of multiple all-in-one printers of the same brand, your boss asks you to work on a new economic printer under the same brand. The difference is that this new printer will have fewer functionalities. As a matter of fact, this printer will only allow printing and scanning.
Considering that this new printer will be marketed under the same brand, you decide to use the same interface.
You define the following class for the new economic printer.
public class LX8590Printer implements AllInOneLXPrinter {
@Override
public boolean print() {
//Logic for printing
return true;
}
@Override
public boolean scan() {
//Logic for scanning
return true;
}
@Override
public boolean fax() {
throw new UnsupportedOperationException("Faxing not supported.");
}
}
This is where the violation of the principle happens. The LX8590Printer depends on a method that will never use. This new printer doesn't allow faxing, so it has to throw an exception instead.
Complying with the Interface Segregation Principle
The main problem with the example above is that we use a general fat interface, instead of specialized interfaces. To solve this problem, we have to segregate the fat interface into three different interfaces.
- AllInOneFaxing
- AllInOneScanning
- AllInOnePrinting
public interface AllInOneFaxing {
public boolean fax();
}
public interface AllInOnePrinting {
public boolean print();
}
public interface AllInOneScanning {
public boolean scan();
}
We modify the first printer class _LX8578Printer _, so it can use the specialized interfaces.
public class LX8578Printer implements AllInOnecanning,
AllInOneFaxing,
AllInOnePrinting{
@Override
public boolean print() {
//Logic for printing
return true;
}
@Override
public boolean scan() {
//Logic for scanning
return true;
}
@Override
public boolean fax() {
//Logic for faxing
return true;
}
}
Now we can define our LX8590Printer without depending upon any method that will never use.
public class LX8590Printer implements AllInOneScanning,
AllInOnePrinting{
@Override
public boolean print() {
//Logic for printing
return true;
}
@Override
public boolean scan() {
//Logic for scanning
return true;
}
}
Now our solution complies with the Interface Segregation Principle.
Difference between Interface Segregation Principle and Liskov Substitution Principle
Some developers may get confused when trying to find the differences between ISP and LSP, but they are completely different. LSP focuses on honoring the contract established by an abstract class, by using subtyping and inheritance. On the other hand, ISP focuses on segregating fat interfaces into specialized interfaces, so a class won't depend on interfaces that won't use.
When to use Interface Segregation Principle
This principle, as same as the Single Responsibility Principle, has the main goal of limiting the scope of future changes. However, it is very difficult, in the first iterations, to identify the critical parts of your solution that will violate the principle. So, my advice is to wait for your code to evolve, so you can identify where you can apply it. Don't try to guess future requirements because there is a chance that you make your code more complicated.
If you like to read more about ISP, you can have a look at Uncle Bob’s Blog.
Top comments (0)