Liskov Substitution Principle (LSP)
Liskov
Substitution Principle (LSP)
Derived
classes should be substitutable for their base classes without changing
the expected behavior of the system.
Explanation:
If a
class S is a subclass of class T, then objects of type T should
be replaceable with objects of type S without breaking the
behavior of the program.
The subclass must honor the expected behavior of the base
class.
Why
it's important:
- Ensures reliable
polymorphism
- Prevents unexpected bugs when
using inheritance
- Makes code easier to maintain and
extend
Payment
Gateway System
Imagine
building an e-commerce platform where customers can pay using
different methods: Credit Card, UPI, or Wallet. You define a base class:
public
class PaymentMethod
{
public
virtual void ProcessPayment(decimal amount)
{
//
process payment logic
}
}
Now
you create specific payment types:
public
class CreditCardPayment : PaymentMethod
{
public
override void ProcessPayment(decimal amount)
{
Console.WriteLine($"Paid
{amount} via Credit Card.");
}
}
public
class UPIPayment : PaymentMethod
{
public
override void ProcessPayment(decimal amount)
{
Console.WriteLine($"Paid
{amount} via UPI.");
}
}
So
far, all is good — both subclasses substitute the base class without changing
behavior.
Violating
LSP Example:
Now,
someone adds a new subclass:
public
class CODPayment : PaymentMethod
{
public
override void ProcessPayment(decimal amount)
{
throw
new NotSupportedException("COD cannot be processed online.");
}
}
This
violates LSP, because code that expects PaymentMethod will crash when
passed a CODPayment. For example:
List<PaymentMethod>
payments = new List<PaymentMethod>
{
new
CreditCardPayment(),
new
UPIPayment(),
new
CODPayment() // Will throw at runtime
};
foreach
(var method in payments)
{
method.ProcessPayment(1000);
// đ„ COD
breaks expectations
}
Solution
(Follow LSP):
Refactor
the design so that only supported payment types are processed:
public
abstract class OnlinePaymentMethod
{
public
abstract void ProcessPayment(decimal amount);
}
public
class CreditCardPayment : OnlinePaymentMethod
{
public
override void ProcessPayment(decimal amount)
{
Console.WriteLine($"Paid
{amount} via Credit Card.");
}
}
public
class UPIPayment : OnlinePaymentMethod
{
public
override void ProcessPayment(decimal amount)
{
Console.WriteLine($"Paid
{amount} via UPI.");
}
}
//
COD is no longer part of OnlinePaymentMethod
public
class CODPayment
{
public
void PayOnDelivery(decimal amount)
{
Console.WriteLine($"Pay
{amount} upon delivery.");
}
}
Now OnlinePaymentMethod subclasses
are 100% substitutable. Code expecting OnlinePaymentMethod will work
safely with any derived class.
Summary
LSP
ensures that subclasses behave as expected, avoiding runtime
surprises or broken contracts. In real-world terms, if a system expects
any PaymentMethod to "process payments," then all
subclasses must actually do so — not throw errors or behave differently.
Comments
Post a Comment