Liskov Substitution Principle – is one of the SOLID principles defined by Barbara Liskov. Principle is based on the parent-child relationship in other words inheritance features of OOD (Object Oriented Design). Principle says “When class S is a subtype of class T then an object of type T can be replaced by an object of type S without affecting functionality/correctness of the implementation or program”.
In simple words it says “Places in implementation (Class/Function) that use a base class, in other words consume a service of a base class, must work correctly when the base class object is replaced by a child class (derived class) object.”
Liskov Substitution Principle in Real life
The following is an example of an electric bulb that actually violates substitution. When the bulb fails it is replaced with a new bulb. Here in this example the old bulb is replaced with the new bulb.
Perfect Substitution
Note:
Here in this example in the family of bulbs, considering the old bulb to be a parent of all bulb types and a CFG bulb is a child of the same family inherited from it.
When one replaces a failed bulb, in other words in programming terms substitutes the old with the new, it must provide light that is provided by the old bulb, in other words works without affecting correctness or functionality (provides a constant light in the house). In the preceding example the substitution worked perfectly since there is no modification in functionality.
Violation Example
The preceding image shows the violation of the principle. Since replacing with a decoration bulb (that provides light in the form of decoration) instead of a bulb meant for just providing light in the house. That actually is a violation in the sense of modifying the functionality because a decoration bulb does not provide the same functionality for the consumer.
Example of not following Principle in Application Development
Continuing with bank savings account that is already explained in previous articles about the Open-Closed Principle.
In the preceding code the IsavingAccount interface is implemented by a different kind of savings account of the bank, like Regular, Salary and FixDeposit savings account.
But as per the banking rules, a FixDeposit savings account doesn't provide a withdrawal facility whereas another bank account might provide a withdrawal facility.
So the developer might write code to raise an exception when an attempt is made to withdraw from a FixDeposit savings account.
Now consider the following method in a class calling a withdrawal by casting the actual object to the parent class type.
The following code calls the method,
Violation of Liskov Substitution Rule
So the preceding code breaks the Liskov Substitution rule since the inherited FixDepositSavingAccount class is modifying the functionality of the withdrawal. Because the savings account should provide functionality to withdraw an amount without throwing any error.
How to stop violating the rule
To stop violating the rule one must verify the inheritance tree, in other words child classes inherited from the parent class should not break the functionality when the child class object replaces the parent class object.
So the class must inherit from the proper parent class in such a way that when the child class replaces the parent, it doesn't break the actual functionality provided by the parent class.
Note
It's not always true that one must make changes in the inheritance tree but making changes at the class and method level also resolves problems. To get such kind of example click on the link: Object Menter (Square and rectangle example).
In the preceding image the new classes WithWithdrawal and WithoutWithdrawal are created and the child classes are inherited from the respective parent class.
So in the preceding code:
Now using it:
Now the following code to call method:
Disadvantage of not following the Liskov Substitution Principle
In simple words it says “Places in implementation (Class/Function) that use a base class, in other words consume a service of a base class, must work correctly when the base class object is replaced by a child class (derived class) object.”
Liskov Substitution Principle in Real life
The following is an example of an electric bulb that actually violates substitution. When the bulb fails it is replaced with a new bulb. Here in this example the old bulb is replaced with the new bulb.
Perfect Substitution
Note:
Here in this example in the family of bulbs, considering the old bulb to be a parent of all bulb types and a CFG bulb is a child of the same family inherited from it.
When one replaces a failed bulb, in other words in programming terms substitutes the old with the new, it must provide light that is provided by the old bulb, in other words works without affecting correctness or functionality (provides a constant light in the house). In the preceding example the substitution worked perfectly since there is no modification in functionality.
Violation Example
The preceding image shows the violation of the principle. Since replacing with a decoration bulb (that provides light in the form of decoration) instead of a bulb meant for just providing light in the house. That actually is a violation in the sense of modifying the functionality because a decoration bulb does not provide the same functionality for the consumer.
Example of not following Principle in Application Development
Continuing with bank savings account that is already explained in previous articles about the Open-Closed Principle.
Interface ISavingAccount { //Other method and property and code bool Withdrwal(decimal amount); } Public Class RegularSavingAccount : ISavingAccount { //Other method and property and code related to Regular Saving account Public bool Withdrwal () { Decimal moneyAfterWithdrawal = Balance-amount; if(moneyAfterWithdrawal >= 1000) { //update balace return true; } else return false; } } Public Class SalarySavingAccount : ISavingAccount { //Other method and property and code related to Salary Saving account` Public bool Withdrwal () { Decimal moneyAfterWithdrawal = Balance-amount; if(moneyAfterWithdrawal >= 0) { //update balace return true; } else return false; } } Public Class FixDepositSavingAccount : ISavingAccount { //Other method and property and code related to Salary Saving account` Public bool Withdrwal () { Throw New Excpetion(“Not supported by this account type”); } }
In the preceding code the IsavingAccount interface is implemented by a different kind of savings account of the bank, like Regular, Salary and FixDeposit savings account.
But as per the banking rules, a FixDeposit savings account doesn't provide a withdrawal facility whereas another bank account might provide a withdrawal facility.
So the developer might write code to raise an exception when an attempt is made to withdraw from a FixDeposit savings account.
Now consider the following method in a class calling a withdrawal by casting the actual object to the parent class type.
Public class AccountManager { Public bool WithdrawFromAccount(IsavingAccount account) { account.Withdraw(amount); } }
The following code calls the method,
//works ok AccountManager.WidhdrawFromAccount(new RegularSavingAccount()); //works ok AccountManager.WidhdrawFromAccount(new SalarySavingAccount()); //throws exception as withdrawal is not supported AccountManager.WidhdrawFromAccount(new FixDepositSavingAccount());
Violation of Liskov Substitution Rule
So the preceding code breaks the Liskov Substitution rule since the inherited FixDepositSavingAccount class is modifying the functionality of the withdrawal. Because the savings account should provide functionality to withdraw an amount without throwing any error.
How to stop violating the rule
To stop violating the rule one must verify the inheritance tree, in other words child classes inherited from the parent class should not break the functionality when the child class object replaces the parent class object.
So the class must inherit from the proper parent class in such a way that when the child class replaces the parent, it doesn't break the actual functionality provided by the parent class.
Note
It's not always true that one must make changes in the inheritance tree but making changes at the class and method level also resolves problems. To get such kind of example click on the link: Object Menter (Square and rectangle example).
In the preceding image the new classes WithWithdrawal and WithoutWithdrawal are created and the child classes are inherited from the respective parent class.
So in the preceding code:
Interface ISavingAccount {} Public Class SavingAccountWithWithdrawal : ISavingAccount { Public virtual bool Withdrwal () {} } Public Class SavingAccountWithoutWithdrawal : ISavingAccount { } Public Class RegularSavingAccount : SavingAccountWithWithdrawal { Public bool Withdrwal () { //implementation } } Public Class SalarySavingAccount : SavingAccountWithWithdrawal { Public bool Withdrwal () {//implementation } } Public Class FixDepositSavingAccount : SavingAccountWithoutWithdrawal { }
Now using it:
Public class AccountManager { Public bool WithdrawFromAccount(SavingAccountWithWithdrawal account) { account.Withdraw(amount); } }
Now the following code to call method:
//works ok AccountManager.WidhdrawFromAccount(new RegularSavingAccount()); //works ok AccountManager.WidhdrawFromAccount(new SalarySavingAccount()); //compiler gives error AccountManager.WidhdrawFromAccount(new FixDepositSavingAccount());
Disadvantage of not following the Liskov Substitution Principle
- Developed code throws a run time error or exception or also might not work as expected and that leads to program failure or incorrect results.
- The preceding discussion shows an example of throwing an exeption by a child class for a not supported method.
- Read link: Object Mentor that shows an example (square and rectangle example) of giving incorrect results when not following the principle.
No comments:
Post a Comment