Adavantages of Liskov Substitution Principle
Code Reusability and Reduced Duplication
LSP, by virtue of its nature, fosters reusability. You can use the superclass object and substitute it with any of its subclasses without worrying about the system behaving unexpectedly.
Let's reference the code we used in the previous section to understand this.
abstract class PaymentProcessor {
abstract processPayment(amount: number): void;
}
class CreditCardProcessor extends PaymentProcessor {
processPayment(amount: number): void {
console.log(`Processing credit card payment of $${amount}`);
// implementation details for processing credit card payment...
}
}
class DebitCardProcessor extends PaymentProcessor {
processPayment(amount: number): void {
console.log(`Processing debit card payment of $${amount}`);
// implementation details for processing debit card payment...
}
}
class PayPalProcessor extends PaymentProcessor {
processPayment(amount: number): void {
console.log(`Processing PayPal payment of $${amount}`);
// implementation details for processing PayPal payment...
}
}
function executePayment(paymentProcessor: PaymentProcessor, amount: number) {
paymentProcessor.processPayment(amount);
}
// Now, we can process payments using any of the payment methods:
const creditCardProcessor = new CreditCardProcessor();
executePayment(creditCardProcessor, 100);
const debitCardProcessor = new DebitCardProcessor();
executePayment(debitCardProcessor, 200);
const payPalProcessor = new PayPalProcessor();
executePayment(payPalProcessor, 300);
All payment processors share
the processPayment
method. If we need to add another type of payment
processor, we don't need to reimplement processPayment
from scratch.
Instead, we just extend the PaymentProcessor
class and provide a specific
implementation for the new payment method. This way, we're reusing the
existing class structure and reducing code duplication.
Enhanced Flexibility
The LSP allows you to make your code more flexible. You can introduce new subclasses without breaking existing functionality, because the contract defined by the superclass is honored by all subclasses.
If we want to add a new payment method, we can
easily do so by extending the PaymentProcessor
class and providing the
specific implementation. The rest of the system can remain the same and still
work with the new payment processor, demonstrating the flexibility of the
design.
Let's say we want to add support for Bitcoin payments. We'd create a
new class BitcoinProcessor
that extends the PaymentProcessor
class:
class BitcoinProcessor extends PaymentProcessor {
processPayment(amount: number): void {
console.log(`Processing Bitcoin payment of ${amount} BTC`);
// Implementation details for processing Bitcoin payment...
}
}
Now we can use BitcoinProcessor
in the same way as any
other PaymentProcessor
:
const bitcoinProcessor = new BitcoinProcessor();
executePayment(bitcoinProcessor, 2); // Process a payment of 2 BTC
The executePayment
function and any other part of the system that
uses PaymentProcessor
objects can work with BitcoinProcessor
without any
changes. This is the power and flexibility provided by adhering to the Liskov
Substitution Principle.
Lower Maintenance Cost
Codebases adhering to the LSP are easier to maintain. Changes to the superclass or subclass will be straightforward because the substitutability ensures a level of consistency in behavior.
Changes to how a specific payment method is
processed only require changes to the corresponding class, not to
the executePayment
function or any other part of the system that
uses PaymentProcessor
objects. This containment of changes makes the system
easier to maintain.
Improved Robustness
Because each payment processor adheres to the Applications are less prone to errors and unexpected behavior because the LSP helps avoid certain design anomalies that might arise from improper subclassing.
Because each payment processor adheres to the contract defined by the PaymentProcessor class, we can be sure that they will behave as expected. This reduces the risk of errors and unexpected behavior.
Time To Transition From JavaScript To TypeScript
Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.
SEE COURSE DETAILSIncreased Understanding and Ease of Use
If subclasses can truly substitute for their base classes, developers can confidently use subclasses in place of their base class, leading to increased understanding and ease of use when working with a hierarchy of classes.
It's clear from the class structure that all payment processors can process a payment. This makes the system easy to understand and use - we don't need to worry about the specifics of each payment method when we're writing code that uses payment processors.
Modularity
Adhering to the LSP often leads to more modular code, since it encourages creating substitutable parts for complex systems. This can make the overall software design cleaner and easier to understand.
The system is made up of interchangeable PaymentProcessor
objects. This modular design makes it easy to adjust the system as needed,
for example by adding or removing payment methods.
In this way, the Liskov Substitution Principle helps us create a system that is flexible, robust, easy to understand and maintain, and well-structured.
What Can You Do Next 🙏😊
If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for software developers.