Interface segregation design principle in java (SOLID & real time example)

  • Interface segregation principle (ISP) is one of the five principles of SOLID acronym.
  • Interface Segregation Principle states “Clients should not be forced to depend upon interfaces that they do not use.”
    • Client should not have the dependency on the interfaces that they do not use.

Let us understand the Interface segregation principle with the help of examples.

1. Example: Interface segregation principle in java

Suppose, we would like to design the calculator display. The calculator can be used any of three different form.

  1. Basic Calculator containing basic mathematical functions like addition, subtraction, multiplication etc
  2. Progrmmers Calculator containing functions like hex to decimal, decimal to binary etc. in addition to basic functions
  3. Scientific Calculator containing functions sin, cos, atan etc in addition to basic functions

We designed a CalculatorDisplay inteface, which caters required functionality of basic, programmer & scientific display. At first glance everything seems okay as CalculatorDisplay is providing the three views for different type of calculators.

2. Program: Application code violating ISP design principle (example)

2.1.) CalculatorDisplay Interface:

  • Interface contains abstract methods of basicView, programmerView &  scientificView.
package org.learn.without.isp;

public interface CalculatorDisplay { 
 void basicView();
 void programmerView();
 void scientificView();
}

2.2.) BasicCalculator Class:

  • BasicCalculator class implements CalculatorDisplay interface.
  • BasicCalculator implements basicView  method.
  • BascicCalulator does not provide the feature of programmer & scientific calculator, henec BasicCalculator does not implement programmerView & scientificView methods.
Fig 1: BasicCalculator Class
package org.learn.without.isp;

import org.apache.commons.lang3.NotImplementedException;

public class BasicCalculator implements CalculatorDisplay {

 @Override
 public void basicView() {
  System.out.println("BasicCalculator: Implemented basic view");
 }

 @Override
 public void programmerView() {
  throw new NotImplementedException("BasicCalculator: programmerView not implemented");
  
 }

 @Override
 public void scientificView() {
  throw new NotImplementedException("BasicCalculator: scientificView not implemented");
 }
}

2.3.) ScientificCalculator Class:

  • ScientificCalculator class implements CalculatorDisplay interface.
  • ScientificCalculator implements basicView & scientificView method.
  • ScientificCalculator does not provide the feature of prorammer calculator, so ScientificCalculator does not implement programmerView method.
Fig 2: ScientificCalculator Class
package org.learn.without.isp;

import org.apache.commons.lang3.NotImplementedException;

public class ScientificCalculator implements CalculatorDisplay {

 @Override
 public void basicView() {
  System.out.println("ScientificCalculator: implemented basic view");
 }

 @Override
 public void scientificView() {
  System.out.println("ScientificCalculator: implemented scientific view");
 }

 @Override
 public void programmerView() {
  throw new NotImplementedException("ScientificCalculator:programmerView not implemented");
 }
}

2.4.) ProgrammerCalculator Class:

  • ProgrammerCalculator class implements CalculatorDisplay interface.
  • ProgrammerCalculator implements basicView & programmerView method.
  • ProgrammerCalculator does not implements scientificView method.
Fig 3: ProgrammerCalculator Class
package org.learn.without.isp;

import org.apache.commons.lang3.NotImplementedException;

public class ProgrammerCalculator implements CalculatorDisplay {

 @Override
 public void basicView() {
  System.out.println("ProgrammerCalculator: implemented basic view");
 }

 @Override
 public void programmerView() {
  System.out.println("ProgrammerCalculator: implemented programmer view");
 }

 @Override
 public void scientificView() {
  throw new NotImplementedException("ProgrammerCalculator:scientificView not implemented");
 }
}

3. Issues with existing design:

  • BasicCalculator class is forced to provide an implementation of the programmerView & scientificView interfaces even though the BasicCalculator doesn’t uses it.
  • ScientificCalculator & ProgrammerCalculator classes are forced to implement the programmerView & scientificView methods respectively.
  • It doesn’t look greate design, its clear violation of interface segregation design principle.

We should revisit our current design approach of CalculatorDisplay. CalculatorDisplay interface seems to contain many interfaces and which are not cohesive. Robert C Martin calls them the ‘fat’ or ‘polluted’ interfaces. These ‘fat’ interfaces introduce unwanted dependencies in the classes e.g. BasicCalculator, ScientificCalculator classes etc. What is the right way to define the interfaces?

4. Application code after moving non-cohesive interfaces (ISP compliant)

4.1.) Redesigned or Cohesive Interfaces: 

  • Segregation the interfaces so that we will have cohesive interfaces.
package org.learn.isp;

public interface BasicDisplay {
 void basicView();
};


package org.learn.isp;

public interface ScientificDisplay {
 void scientificView();
};


package org.learn.isp;

public interface ProgrammerDisplay {

 void programmerView();
};

What we have achieved from above segregation?

  • We have separate interfaces for each type of displays
    • BasicDisplay, ProgrammerDisplay and ScientificDisplay.
  • We have split out our Fat interface i.e. CalculatorDisplay to corresponding cohesive interfaces (BasicDisplay, ScientificDisplay & ProgrammerDisplay).

4.2.) BasicCalculator Class: 

  • BasicCalculator Class implements the BasicDisplay interface & our class looks fine now.
Fig 4: BasicCalculator Class
package org.learn.isp;

public class BasicCalculator implements BasicDisplay {

 @Override
 public void basicView() {
  System.out.println("BasicCalculator: implemented basic view");
 }
}

4.3.) ScientificCalculator Class:

  • ScientificCalculator Class implements the BasicDisplay & ScientificDisplay interfaces.
Fig 5: ScientificCalculator Class
package org.learn.isp;

public class ScientificCalculator implements ScientificDisplay,BasicDisplay {

 @Override
 public void basicView() {
  System.out.println("ScientificCalculator: implemented basic view");
 }
 @Override
 public void scientificView() {
  System.out.println("ScientificCalculator: implemented scientific view");
 }
}

4.4.) ProgrammerCalculator Class:

  • ProgrammerCalculator Class implements the BasicDisplay & ProgrammerDisplay interfaces.
interface segregation design principle example
Fig 6: ProgrammerCalculator Class
package org.learn.isp;

public class ProgrammerCalculator implements ProgrammerDisplay, BasicDisplay {

 @Override
 public void basicView() {
  System.out.println("ProgrammerCalculator: implemented basic view");
 }

 @Override
 public void programmerView() {
  System.out.println("ProgrammerCalculator: implemented programmer view");
 }
}

5. Conclusion:

  • Now clearly, the concrete classes does not contains the unwanted methods.
  • Classes doesn’t have to implement the interfaces, which they do not use.
    • There are no dependencies to unwanted interfaces.
  • Our design adhering to interface segregation design principle.

Download example-Interface segregation design principle

 

Scroll to Top