SOLID: Liskov Substitution Principle
Tags: java, oop, programming, architecture
Published: 2021-04-27 18:44:28

_This is a continuation of the [SOLID principles] (https://dev.to/victorpinzon1988eng/solid-single-responsibility-principle-48an) series._

The Liskov substitution principle is the most technical principle of all. However, it is the one that most helps to develop decoupled applications, which is the foundation of designing reusable components.

Barbara Liskov defined this principle as follows: 

>Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T”

The definition given by Liskov is based on the Design by Contract (DbC) defined by Bertrand Meyer. A contract that is identified by preconditions, invariants, and postconditions:

* A routine can expect a certain condition to be guaranteed on entry by any client module that calls it: the routine’s precondition. This is an obligation for the client and benefit for the supplier, as it frees it from having to handle cases outside of the precondition.
* A routine can guarantee a certain property on exit: the routine’s postcondition - an obligation for the supplier, and a benefit for the client.
* Maintain a certain property, assumed on entry and guaranteed on exit: the class invariant.

The concept of contract and implementation is the foundation for inheritance and polymorphism in object-oriented programming.

 In 1996, Robert C. Martin redefined the concept given by Liskov, as follows:

>Function that use pointers of references to base classes must be able to use objects of derived classes without knowing it.

The redefinition given by Bob Martin helped to simplify the concept implemented by Liskov years before and its adoption by developers.

##Violation of the Liskov Substitution Principle
As a developer for a banking entity, you are requested to implement a system for managing bank accounts. Your boss asks you to implement, in the first sprint of the project, a system for managing basic and premium bank accounts. The difference between them is that the latter accumulates preference points on any deposit.

You implement the following abstract class as the foundation of your system.

```java
public abstract class BankAccount {
    
    /**
     * In charge of depositing a specific amount into the account.
     * @param amount            Dollar ammount.
     */
    public abstract void deposit(double amount);
    
    /**
     * In charge of withdrawing a specific amount from the account.
     * @param amount            Dollar amount.
     * @return                  Boolean result.
     */
    public abstract boolean withdraw(double amount);
}
```

This abstract class defines an obligation for any derived class to override any abstract method defined in the BankAccount class. This means that the basic and premium accounts must override the deposit and withdrawal method.

```java
public class BasicAccount extends BankAccount {

    private double balance;
    
    @Override
    public void deposit(double amount) {
        this.balance += amount;
    }

    @Override
    public boolean withdraw(double amount) {
        if(this.balance < amount)
            return false;
        else{
            this.balance -= amount;
            return true;
        }       
    }
}
```

```java
public class PremiumAccount extends BankAccount {

    private double balance;
    private int preferencePoints;
    
    @Override
    public void deposit(double amount) {
        this.balance += amount;
        accumulatePreferencePoints();
    }

    @Override
    public boolean withdraw(double amount) {
         if(this.balance < amount)
            return false;
        else{
            this.balance -= amount;
            accumulatePreferencePoints();
            return true;
        }
    }
    
    private void accumulatePreferencePoints(){
        this.preferencePoints++;
    }
    
}
```

_Please take into account that any of these classes have the minimum validations for a production environment._

All basic and premium accounts are discounted by $25.00 annually for administrative expenses. To implement this policy you defined the following class:

```java
public class WithdrawalService {
    
    public static final double ADMINISTRATIVE_EXPENSES_CHARGE = 25.00;
    
    public void cargarDebitarCuentas(){
        
        BankAccount basiAcct = new BasicAccount();
        basiAcct.deposit(100.00);
        
        BankAccount premiumAcct = new PremiumAccount();
        premiumAcct.deposit(200.00);
        
        List<BankAccount> accounts = new ArrayList();
        
        accounts.add(basiAcct);
        accounts.add(premiumAcct);
        
        debitAdministrativeExpenses(accounts);
        
    }
 
    private void debitAdministrativeExpenses(List<BankAccount> accounts){
        accounts.stream()
                .forEach(account -> account.withdraw(WithdrawalService.ADMINISTRATIVE_EXPENSES_CHARGE));
    }
}
```

On the second sprint of your project, your boss asks you to implement long-term accounts into your bank account managing system. The differences between long-term accounts and basic/premium accounts are the following:

* Long-term accounts are exempt from administrative expenses.
* Long-term accounts don't allow withdrawals. If the client wants to withdraw any amount of his / her account must be done through a different process.

As a developer in charge of the accounts system, you decide to extend the BankAccount class for the Long-term accounts.

```java
public class LongTermAccount extends BankAccount {
    
    private double balance;

    @Override
    public void deposit(double amount) {
        this.balance += amount;
    }

    @Override
    public boolean withdraw(double amount) {
        throw new UnsupportedOperationException("Not supported yet."); 
    }
}
```

This part is where the violation of the Liskov Substitution Principle is obvious. You cannot extend the BankAccount class in the LongTermAccount without overriding the withdrawal method. However, the long-term accounts don’t allow withdrawals according to your project’s requirements. 

You have the following two options to solve this issue:

* You can override the withdrawal method as an empty method or you can throw an UnsupportedOperationException. However, the BankAccount objects wouldn't be completely interchangeable with LongTermAccount objects because if we try to execute the withdrawal method we would get an exception. As a solution for this issue, we can condition the debitAdministrativeExpenses method, so we can skip the LongTermAccount objects but this would violate the Open/Closed Principle. For instance: 

```java
private void debitAdministrativeExpenses(List<BankAccount> accounts){
        
        for(BankAccount account : accounts){
            if(account instanceof LongTermAccount)
                continue;
            else
                account.withdraw(ADMINISTRATIVE_EXPENSES_CHARGE);
        }
    }
```

* You can make your code Liskov Substitution Principle compliant.

##Implementing Liskov Substitution Principle
The main issue with the bank account structure is that the long-term account is not a regular bank account, at least is not the type defined in the BankAccount abstract class. There is a simple test on the abductive reasoning area that can be used to check if a class is a subtype from “X” type. The duck test states “If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck”. The long-term account looks like a regular bank account but it does not behave like a regular one. To solve this issue we have to change the current class structure.

To make our code LSP compliant, we’ll make the following changes:

* All types of bank accounts will allow the deposit action.
* Only the basic and premium bank accounts will allow the withdrawal action.
* We’ll define an abstract bank account for all types of accounts. This abstract class will define only one method, the deposit method.
* We’ll extend the BankAccount with WithdrawableAccount abstract class, which will define the debit method.
* The basic and premium accounts will extend the WithdrawableAccount abstract class, while the long-term account will extend the BankAccount abstract class.

The abstract BankAccount class will define the deposit method.

```java
public abstract class BankAccount {
    
    /**
     * In charge of depositing a specific amount into the account.
     * @param amount            Dollar ammount.
     */
    public abstract void deposit(double amount);
}
```

The abstract WithdrawableAccount class will define the withdrawal method.

```java
public abstract class WithdrawableAccount extends BankAccount {
    
    /**
     * In charge of withdrawing a specific amount from the account.
     * @param amount            Dollar amount.
     * @return                  Boolean result.
     */
    public abstract boolean withdraw(double amount);
}
```

The basic and premium account classes will extend the WithdrawableAccount class, which extends the BankAccount class. This nested inheritance allows the basic/premium accounts to have both methods, deposit, and withdrawal.

```java
public class BasicAccount extends WithdrawableAccount {

    private double balance;
    
    @Override
    public void deposit(double amount) {
        this.balance += amount;
    }

    @Override
    public boolean withdraw(double monto) {
        if(this.balance < monto)
            return false;
        else{
            this.balance -= monto;
            return true;
        }       
    }   
}
```
```java
public class PremiumAccount extends WithdrawableAccount {

    private double balance;
    private int preferencePoints;
    
    @Override
    public void deposit(double monto) {
        this.balance += monto;
        accumulatePreferencePoints();
    }

    @Override
    public boolean withdraw(double monto) {
         if(this.balance < monto)
            return false;
        else{
            this.balance -= monto;
            accumulatePreferencePoints();
            return true;
        }
    }
    
    private void accumulatePreferencePoints(){
        this.preferencePoints++;
    }
}
```

The WithdrawalService class is implemented using only WithdrawableAccount types or subtypes.

```java
public class WithdrawableService {
    
    public static final double ADMINISTRATIVE_EXPENSES_CHARGE = 25.00;
    
    public void cargarDebitarCuentas(){
        
        WithdrawableAccount basicAcct = new BasicAccount();
        basicAcct.deposit(100.00);
        
        WithdrawableAccount premiumAcct = new PremiumAccount();
        premiumAcct.deposit(200.00);
        
        List<WithdrawableAccount> accounts = new ArrayList();
        
        accounts.add(basicAcct);
        accounts.add(premiumAcct);
        
        debitarGastosAdmon(accounts);
        
    }
 
    private void debitarGastosAdmon(List<WithdrawableAccount> accounts){
        accounts.stream()
                .forEach(account -> account.withdraw(WithdrawableService.ADMINISTRATIVE_EXPENSES_CHARGE));
    }
}
```

The changes done on the class structure assures that our code is LSP compliant. Now we are not required to implement the withdrawal method on the LongTermAccount class. Also, we interchange the WithdrawableAccount objects with any subtype of this abstract class. The class structure is also OCP compliant because if we added another bank account type that does not allow withdrawal, we wouldn't need to modify the current code just to extend the BankAccount type. 

##Importance of the Liskov Substitution Principle
The LSP allows us to identify incorrect generalization areas done during the design phase and correct them. The Liskov Substitution Principle is fundamental in the development of the dependency injection concept, which is widely used in Java Enterprise Edition and Spring Framework.

You can use the following tips if you want to easily detect violations of the Liskov Substitution Principle:

There is an LSP violation if you introduce a condition using the type of your object, as shown above in the instanceof example.
There is an LSP violation if you extend an abstract class and you set one of the abstract methods as an empty method or you throw a not defined exception.

##Why not using interfaces instead of abstract classes

Interfaces and abstract classes are very similar, but they are not the same. Abstract classes define functionality that subclasses must implement. On the other hand, interfaces define functionality that must be implemented by any class that implements the given interface.

For instance, abstract classes define that a pony gallops because it is a type of horse. In contrast, an interface defines that a "thing" can "move" because there is an agreement, by implementing the interface, that the thing must move.

So this raises two questions:

* Could we implement the bank account example using interfaces? Yes, Sure. There is no wrong answer here. You could implement the example by using either abstract classes or interfaces.

* Would I still violate LSP by using interfaces? 
Well, there is no correct answer here. LSP was defined in the context of subtyping. However, I think, this is not valid anymore. LSP is not about abstract classes or interfaces, is all about honoring the contract. 

If you like to read more about LSP, you can have a look at [Uncle Bob’s Blog] (https://blog.cleancoder.com/uncle-bob/2020/10/18/Solid-Relevance.html).

In the next post, we will talk about the Interface Segregation Principle.

You can follow me on [Twitter] (https://twitter.com/victorpinzon198) or [Linkedin] (https://www.linkedin.com/in/victor-manuel-pinzon-reyes-072251134/).