Understanding Liskov Substitution Principle with Real Construction Examples
Understanding Liskov Substitution Principle with Real Construction Examples
The Liskov Substitution Principle (LSP) ensures that a subclass can stand in for its parent class without introducing errors or unexpected behavior. In the context of construction project management—such as invoice processing, payments, and expense tracking—LSP helps ensure your system remains stable, extensible, and reliable.
LSP Definition: Objects of a subclass should be replaceable for objects of the base class without changing the correctness of the program.
🔑 Key Rules of LSP
- Behavior Preservation: Subclass behavior must match expectations of the base class.
- No Contradictions: Subclasses should not override methods with conflicting behavior.
- No Side Effects: Substituting subclass must not break logic.
- Contract Adherence: Preconditions must not be tightened; postconditions must be honored.
- Safe Polymorphism: Derived class can be used wherever base class is used.
🏗 Example 1: Behavior Preservation
class PaymentMethod
{
public virtual string ProcessPayment(decimal amount) => $"Paid {amount}";
}
class BankTransfer : PaymentMethod
{
public override string ProcessPayment(decimal amount) => $"Bank Transfer: {amount} successful";
}
void PayContractor(PaymentMethod method, decimal amount)
{
Console.WriteLine(method.ProcessPayment(amount));
}
✅ Result: No surprise behavior. BankTransfer behaves like a PaymentMethod.
🚫 Example 2: Contradiction Violation
class CreditPayment : PaymentMethod
{
public override string ProcessPayment(decimal amount)
{
throw new InvalidOperationException("Credit payment not allowed");
}
}
⚠️ This breaks LSP because the subclass throws an exception unexpectedly.
✅ Fix: Refactor using interfaces for incompatible behavior:
interface IPaymentMethod
{
string ProcessPayment(decimal amount);
}
class CreditPayment
{
public string RequestApproval(decimal amount) => $"Credit approval needed for {amount}";
}
🚫 Example 3: Side Effects in Subclass
class Expense
{
public virtual decimal Amount { get; set; }
}
class MaterialExpense : Expense
{
public override decimal Amount
{
get => base.Amount;
set
{
base.Amount = value;
SendNotification(); // Side effect!
}
}
private void SendNotification()
{
Console.WriteLine("Email sent for material expense.");
}
}
⚠️ Side effects like notifications in property setters violate LSP.
✅ Fix: Use explicit methods to separate concerns.
class MaterialExpense : Expense
{
public void SetAmountAndNotify(decimal amount)
{
Amount = amount;
SendNotification();
}
private void SendNotification()
{
Console.WriteLine("Email sent for material expense.");
}
}
🚫 Example 4: Contract Violation in Invoice System
class Invoice
{
public virtual void Submit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Amount must be positive");
Console.WriteLine("Invoice submitted");
}
}
class OnlineInvoice : Invoice
{
public override void Submit(decimal amount)
{
if (amount < 1000)
throw new ArgumentException("Online invoice must be ≥ 1000");
base.Submit(amount);
}
}
⚠️ OnlineInvoice introduces stricter rules than the base class. This breaks LSP.
✅ Fix: Enforce such rules externally, not in subclasses.
class OnlineInvoice : Invoice
{
public override void Submit(decimal amount)
{
base.Submit(amount);
Console.WriteLine("Online confirmation sent");
}
}
✅ Example 5: Safe Polymorphism with Expenses
class Expense
{
public virtual string GetDetails() => "Generic expense";
}
class LabourExpense : Expense
{
public override string GetDetails() => "Labour charges";
}
class TransportExpense : Expense
{
public override string GetDetails() => "Transport of materials";
}
void PrintExpenseDetails(Expense expense)
{
Console.WriteLine(expense.GetDetails());
}
✅ Result: You can pass any type of Expense to the method, and behavior remains consistent.
🔚 Final Thoughts
The Liskov Substitution Principle ensures that your construction system remains robust even as complexity increases. Violating LSP often leads to bugs, tight coupling, and unexpected failures when working with polymorphism.
✅ Ask yourself: “Can I replace the base class with this subclass without any surprises?” If not, refactor!
🛠 Benefits of LSP
- Prevents unpredictable subclass behavior
- Makes code easier to extend and test
- Promotes cleaner design with clear responsibilities
📘 Want More?
- Need a SOLID Principle cheat sheet?
- Want to implement LSP in a real .NET construction invoice app?
Let me know!

Comments
Post a Comment