SOLID Interface segregation principle

This principle about Interface segregation principle (ISP) says that any client should not be required to depend on methods they do not use.

This principle is quite easy to understand. If you usually use interfaces, then it’s very likely that you’re applying this principle.

Any client should not be required to depend on methods they do not use. What does this mean?

In an existing interface, we should not add new methods that require implementing additional functionality.

Instead of adding methods to an existing interface, it is better to create another interface. In this way, the new interface is implemented only if the new method is required.

The following is an example of incorrect interface use that violates this principle.

Poorly implemented code where we DO NOT use this principle of interface segregation

See the code below for examples where we break this principle.

ISP

Interface Vehicle

// Interface segregation violation
interface Vehicle {
    void start();

    void accelerate();

    void brake();

    void fly();
}

Class Car implements Vehicle

class Car implements Vehicle {

    // Implementation of methods specific to Cars
    public void start() {
        // Start the car
    }

    public void accelerate() {
        // Accelerate the car
    }

    public void brake() {
        // Brake the car
    }

    // Flying is not relevant to Cars, but still implemented due to the interface contract.
    public void fly() {
        // Fly the car? Doesn't make sense!
        throw new UnsupportedOperationException("I can't fly :(");
    }

}

Airplane implements Vehicle

class Airplane implements Vehicle {

    // Implementation of methods specific to Airplanes
    public void start() {
        // Start the airplane
    }

    public void accelerate() {
        // Accelerate the airplane
    }

    public void brake() {
        // Brake the airplane
    }

    public void fly() {
        // Fly the airplane
    }

}

In the above example, Vehicle interface violates the Interface Segregation Principle.

The fly() method does not make sense for the Car class, but it is still required to implement it because it is part of the Vehicle interface.

This leads to unnecessary coupling and forces clients of Car to depend on a method that is not relevant to them.

Running the previous code:

public class Demo {

    public static void main(String[] args) {

        Vehicle car = new Car();
        car.start();
        car.accelerate();
        car.fly(); // car can't fly 
        
        Vehicle airplane = new Airplane();
        airplane.start();
        airplane.accelerate();
        airplane.fly(); // ok

    }

}

How to implement the Interface segregation principle

To adhere to the Interface Segregation Principle, we can split the Vehicle interface into smaller, more specialized interfaces.

In this way, we do NOT force any class to implement the method that it will not use.

ISP

interface Drivable

interface Drivable {
    void start();

    void accelerate();

    void brake();
}

interface Flyable

interface Flyable {
    void fly();
}

Car implements Drivable

class Car implements Drivable {

    // Implementation of methods specific to Cars
    public void start() {
        // Start the car
    }

    public void accelerate() {
        // Accelerate the car
    }

    public void brake() {
        // Brake the car
    }

}

Airplane implements Drivable, Flyable

class Airplane implements Drivable, Flyable {

    // Implementation of methods specific to Airplanes
    public void start() {
        // Start the airplane
    }

    public void accelerate() {
        // Accelerate the airplane
    }h

    public void brake() {
        // Brake the airplane
    }

    public void fly() {
        // Fly the airplane
    }

}

In the updated example, we have separated the responsibilities by creating Drivable and Flyable interfaces.

Now, the clients can depend on only the interfaces they require. The Car class only implements the Drivable interface, and the Airplane class implements both, the Drivable and Flyable interfaces.

Running our new fixed code:

public class Demo {

    public static void main(String[] args) {
        Car car = new Car();
        car.start();
        car.accelerate();

        Airplane airplane = new Airplane();
        airplane.start();
        airplane.accelerate();
        airplane.fly();

    }

}

Conclusion

ISP has the following advantages:

  • Reduces coupling: The system is more decoupled and simpler to manage when clients are only attached to the interfaces they require.
  • Boosts flexibility: It is simpler to introduce new features or modify current ones without disrupting the system when interfaces are more specific and smaller.
  • Enhances code reuse: It is simpler to reuse code across many system components when interfaces are more concise and specialized.

By adhering to the Interface Segregation Principle, we achieve better cohesion, loose coupling, and improved maintainability in our code.

You can review these examples at GitHub

Hi! If you find my posts helpful, please support me by inviting me for a coffee :)

See also