Interface Segregation Principle

The interface segregation principle (ISP) concerns how clients access the functionality developed in another class. It states, “do not force any client to implement...

The interface segregation principle (ISP) concerns how clients access the functionality developed in another class. It states, “do not force any client to implement an interface which is irrelevant to them.”

This means that it is better to have many interfaces than a generalized fat interface that any class can use (The fat interface is an anti-pattern that applies to modules/classes with a large interface with too many exposed functions/methods). It is good to have many client interfaces.

Similar to the Single Responsibility Principle, the ISP states that we need to split larger interfaces into smaller and more specific ones so that the clients need not implement methods that they do not need.”Client” in this case means the implementing class of an interface.

Anytime you’re writing an interface and adding a method to it, ask yourself if you should be creating another interface in its place that is more specific to the client.

The single responsibility principle states that every module, class, or function should have responsibility over a single part of the functionality provided by the software and that the class should entirely encapsulate responsibility.

The ISP is intended to keep a system decoupled and thus easier to change, refactor and redeploy.

Example

Let us consider a simple banking application.

  • Assume a certain bank can have two types of customers: SavingsCustomer and DematCustomer.
  •  SavingsCustomer can deposit money and withdraw money
  • Where in DematCustomers can have both savings accounts and Demat accounts. This account allows customers to deposit money, withdraw money, buy stocks, and sell stocks.

This application has an interface called Customer, and both SavingsCustomer and DematCustomer inherit this interface.

/*This is customer interface and both savingsCustomer and DematCustomer  
implements this interface */
public interface Customer{
    public void withdrawMoney();
    public void depositCash();
    public void buyStock();
    public void sellStock();
}

As DematCustomer class requires buyStock() and sellStock( ) method, we can directly implement Customer class. However, SavingsCustomer class does not require buyStock(), and sellStock( ) as savingCustomers are not allowed to buy and sell stock.

This is a clear violation of an ISP. This results in multiple problems

  • SavingsCustomer does not need all the methods (buyStock and sellStock of the interface but is forced to implement the methods (methods that are empty or throw an exception when called)
  • developers implementing SavingsCustomer class may get confused by methods that they do not need.
  • SavingsCustomer class are forced to recompile if buyStock() and sellStock( ) don’t use change.
  • if DematCustomer requirements change, then we may need to change buyStock() and sellStock( ), and this forces savingsCustomer to be updated even though it did not require functionality

The interface segregation principle recommends proper designing of classes and interfaces with strict enforcement of what is required in a class or an interface and what resides within only must be only related. Hence we need to segregate the interface based on the needs of SavingsCustomer and DematCustomer,

/*Interface for SavingsCustomer */                                         
public interface SCustomer{
    public void withdrawMoney();
    public void depositCash();
}

/*Interface fr DematCustomer */
public interface DCustomer{
    public void buyStock();
    public void sellStock();
}

public class SavingsCustomer implements SCustomer{
    public void withdrawMoney(){
        //logic goes here
    }
    public void depositCash(){
        //logic goes here
    }
}

public class DematCustomer implements SCustomer, DematCustomer
{
    public void withdrawMoney(){
        //logic goes here
    }
    
    public void depositCash(){
        //logic goes here
    }
    
    public void buyStock(){
        //logic goes here
    }
    
    public void sellStock(){
        logic goes here
    }
}

We have segregated the interface based on the client’s needs in the above code snippet.

Note: This is not a perfect design; the refactoring example code above is just for demo purposes and breaks other SOLID principles and design patterns. Further refactoring of this example is necessary for better design.

Related Articles