Created by Stan on 18-04-2023
The Liskov Substitution Principle (LSP) is one of the five principles of object-oriented programming and design known as SOLID. It was introduced by Barbara Liskov and states that objects of a derived class should be able to replace objects of the base class without affecting the correctness of the program. In this article, we will explore the LSP and demonstrate its application in Ruby using a real-world example.
The LSP helps to ensure that a subclass can always be substituted for its superclass without affecting the overall behavior of the program. It guarantees that a derived class will maintain the same contracts as its base class, including method signatures, return types, and expected behavior.
Adhering to the LSP ensures that your code is robust, maintainable, and easy to understand. When a class hierarchy follows the LSP, it becomes much simpler to reason about the behavior of individual classes and their relationships.
Consider an e-commerce application that supports multiple payment methods, such as credit cards and PayPal. We can use the LSP to create a flexible and maintainable design for processing payments.
class PaymentProcessor def process(payment_method, amount) payment_method.charge(amount) end end
class CreditCard def charge(amount) puts "Charging credit card with amount: #{amount}" # Code to charge the credit card # In a real-world scenario, this would involve API calls to the payment gateway true end end
class PayPal def charge(amount) puts "Charging PayPal account with amount: #{amount}" # Code to charge the PayPal account # In a real-world scenario, this would involve API calls to the PayPal API true end end
In this example, we have a PaymentProcessor
class that accepts a payment_method
object and an amount
as arguments. The payment_method
object should have a charge
method that takes the amount as an argument. Both the CreditCard
and PayPal
classes implement this charge
method, allowing them to be used interchangeably as payment methods.
This design adheres to the LSP, as the CreditCard
and PayPal
classes can be substituted for each other without affecting the correctness of the program.
Now let's test the code.
payment_processor = PaymentProcessor.new credit_card = CreditCard.new paypal = PayPal.new payment_processor.process(credit_card, 50) payment_processor.process(paypal, 50)
At first glance, this implementation might seem reasonable, as a gift card requires an additional piece of information (a PIN) to process a payment. However, it violates the LSP because the GiftCard class doesn't maintain the same contract as the other payment method classes.
If we try to use the GiftCard
class with the existing PaymentProcessor
, the program will raise an error due to the mismatch in method signatures:
payment_processor = PaymentProcessor.new gift_card = GiftCard.new payment_processor.process(gift_card, 50) # Raises an ArgumentError: wrong number of arguments
To fix the LSP violation, we can modify our class hierarchy to ensure that the contracts are maintained. In this case, we can update the GiftCard class to accept the PIN in its initializer, allowing it to maintain the same charge method signature as the other payment methods:
class GiftCard def initialize(pin) @pin = pin end def charge(amount) puts "Charging gift card with amount: #{amount}, using PIN: #{@pin}" # Code to charge the gift card # In a real-world scenario, this would involve API calls to the gift card provider true end end
Now, the GiftCard
class follows the LSP and can be used interchangeably with the other payment methods:
payment_processor = PaymentProcessor.new gift_card = GiftCard.new(1234) payment_processor.process(gift_card, 50)
Coding
Posted on 07 Apr, 2023Coding
Posted on 07 Apr, 2023Coding
Posted on 07 Apr, 2023