SOLID — Interface Segregation Principle

OmerM
3 min readSep 24, 2022

--

Photo by Luca R on Unsplash

This principle corresponds to ‘I’ in the SOLID abbreviation.

Clients in SW Architecture should not depend on methods that they do not use.

In other words, big interfaces should be split into smaller relevant interfaces.

If there is one big interface when new functionality is added to that interface, all classes that implement this interface would be forced to implement new functionality regardless of whether they support it or not.

If there is a dependency between two classes, one class should depend on another class via the smallest possible interface.

When this principle is NOT followed, client developers would be confused by methods they do not need.

The sample codes can be seen below.

Sample

1st Iteration

package sampleFirstIteration;

public interface BaseInterface {

public void doSomething();
public void performSomething();

}
package sampleFirstIteration;

public class SubClass1 implements BaseInterface {

public void doSomething() {
...
}

public void performSomething() {
...
}

}
package sampleFirstIteration;

public class SubClass2 implements BaseInterface {

public void doSomething() {
...
}

public void performSomething() {
...
}

}
// Application Class 
// Sample's 1st Iteration

package sampleFirstIteration;

import java.util.ArrayList;

public class Application {

public static void main(String[] args) {
ArrayList<BaseInterface> list = new ArrayList<>();
list.add(new SubClass1());
list.add(new SubClass2());

for(BaseInterface base : list) {
base.doSomething();
base.performSomething();
}
}

}

2nd Iteration

At this point, there is a need of another class.
This class supports doSomething functionality but does not support performSomething method. So new Class’s performSomething method throws UnsupportedOperationException.
That is not good according to principle.

package sampleSecondIteration;

import sampleFirstIteration.BaseInterface;

public class SubClass3 implements BaseInterface {

public void doSomething() {
...
}

public void performSomething() {
throw new UnsupportedOperationException(
"SubClass3 - UNSUPPORTED FUNCTIONALITY!");
}

}
// Application Class
// Sample's 2nd Iteration

package sampleSecondIteration;

import java.util.ArrayList;

import sampleFirstIteration.BaseInterface;
import sampleFirstIteration.SubClass1;
import sampleFirstIteration.SubClass2;

public class Application {

public static void main(String[] args) {
ArrayList<BaseInterface> list = new ArrayList<>();
list.add(new SubClass1());
list.add(new SubClass2());
list.add(new SubClass3());

for(BaseInterface base : list) {
base.doSomething();
base.performSomething();
}
}

}

3rd Iteration

The solution to the problem in the previous section is — as principle tells -
segregating interfaces.

package sampleThirdIteration;

public interface Doable {

public void doSomething();

}
package sampleThirdIteration;

public interface Performable {

public void performSomething();

}
package sampleThirdIteration;

public interface BaseInterface extends Doable, Performable {

}
package sampleThirdIteration;

public class SubClass1 implements BaseInterface {

public void doSomething() {
...
}

public void performSomething() {
...
}

}
package sampleThirdIteration;

public class SubClass2 implements BaseInterface {

public void doSomething() {
...
}

public void performSomething() {
...
}

}

Please pay attention here, SubClass3 implements Doable Interface rather than BaseInterface.

package sampleThirdIteration;

public class SubClass3 implements Doable {

public void doSomething() {
...
}

}
// Application Class
// Sample's 3rd Iteration

package sampleThirdIteration;

import java.util.ArrayList;

public class Application {

public static void main(String[] args) {
ArrayList<BaseInterface> list1 = new ArrayList<>();
list1.add(new SubClass1());
list1.add(new SubClass2());

// The one line code below gives compiler error
// due to SubClass3 only implements Doable Interface
//list.add(new SubClass3());

// ITERATING THROUGH DOABLE AND PERFORMABLE OBJECTS
for(BaseInterface base : list1) {
base.doSomething();
base.performSomething();
}

ArrayList<Doable> list2 = new ArrayList<>();
list2.add(new SubClass1());
list2.add(new SubClass2());
list2.add(new SubClass3());

// ITERATING THROUGH ONLY DOABLE OBJECTS
for(Doable doable : list2) {
doable.doSomething();
}
}

}

This is the end of the article. Please check the next article which is about the Dependency Inversion Principle.

Thanks for reading.

--

--

OmerM

Senior Software Engineer, sharing my knowledge and what i learn.