Introduction

In this article, we will cover the Liskov Substitution Principle (LSP).

Liskov Substitution Principle (LSP)

Let’s take another example of Bank Accounts.

Suppose you have two types of bank accounts: a RegularAccount and a FixedTermDepositAccount. The FixedTermDepositAccount doesn’t allow withdrawals until the end of the term.

Violation of LSP/Bad Code

using System;
namespace DesignPattern
{
    public class BankAccount
    {
        protected double balance;

        public virtual void Deposit(double amount)
        {
            balance += amount;
        }

        public virtual void Withdraw(double amount)
        {
            if (balance >= amount)
            {
                balance -= amount;
            }
            else
            {
                throw new InvalidOperationException("Insufficient funds");
            }
        }

        public double GetBalance()
        {
            return balance;
        }
    }

    public class FixedTermDepositAccount : BankAccount
    {
        public override void Withdraw(double amount)
        {
            throw new InvalidOperationException("Cannot withdraw from a fixed term deposit account until term ends");
        }
    }
}

Note. If a FixedTermDepositAccount object is substituted for a BankAccount object in a context that expects withdrawals to be possible, the program will break.

Using LSP / Good Code

using System;
namespace DesignPattern
{
    public abstract class BankAccount
    {
        protected double balance;

        public virtual void Deposit(double amount)
        {
            balance += amount;
            Console.WriteLine($"Deposit: {amount}, Total Amount: {balance}");
        }

        public abstract void Withdraw(double amount);

        public double GetBalance()
        {
            return balance;
        }
    }

    public class RegularAccount : BankAccount
    {
        public override void Withdraw(double amount)
        {
            if (balance >= amount)
            {
                balance -= amount;
                Console.WriteLine($"Withdraw: {amount}, Balance: {balance}");
            }
            else
            {
                Console.WriteLine($"Trying to Withdraw: {amount}, Insufficient Funds, Available Funds: {balance}");
            }
        }
    }

    public class FixedTermDepositAccount : BankAccount
    {
        private bool termEnded = false; // simplification for the example

        public override void Withdraw(double amount)
        {
            if (!termEnded)
            {
                Console.WriteLine("Cannot withdraw from a fixed term deposit account until term ends");
            }
            else if (balance >= amount)
            {
                balance -= amount;
                Console.WriteLine($"Withdraw: {amount}, Balance: {balance}");
            }
            else
            {
                Console.WriteLine($"Trying to Withdraw: {amount}, Insufficient Funds, Available Funds: {balance}");
            }
        }
    }
    
    //Testing the Liskov Substitution Principle
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("RegularAccount:");
            var RegularBankAccount = new RegularAccount();
            RegularBankAccount.Deposit(1000);
            RegularBankAccount.Deposit(500);
            RegularBankAccount.Withdraw(900);
            RegularBankAccount.Withdraw(800);

            Console.WriteLine("\nFixedTermDepositAccount:");
            var FixedTermDepositBankAccount = new FixedTermDepositAccount();
            FixedTermDepositBankAccount.Deposit(1000);
            FixedTermDepositBankAccount.Withdraw(500);
            
            Console.ReadKey();
        }
    }
}

LSP

When designing the BankAccount class, we make the Withdraw method abstract to enable derived types might have their own rules for withdrawal. This approach helps the client code understand that subclasses may have varying behaviors, and Liskov Substitution Principle (LSP) is not violated. When you run the above code.