Liskov Substitution Principle (LSP)
The Liskov Substitution Principle (LSP) is a fundamental principle in object-oriented programming that states that objects of a superclass should be able to be replaced with objects of a subclass without affecting the correctness of the program. This principle is named after Barbara Liskov, who first formulated it in a 1987 paper.
Definition:
Let φ(x) be a property provable about objects x of type T. Then φ(y) should also be true for objects y of type S where S is a subtype of T — Wikipedia
In simple words,
- Objects of a superclass should be able to be replaced with objects of a subclass without affecting the program.
- Object of subclass should be able to access the all the methods and properties of the superclass.
Let’s explore two examples.
Example 1:
Here, Green is a base super class of Blue. When a new object of Blue will be assigned in object of Super class, getColor() of Green will be replaced by getColor() of Blue according to inheritance rules of OOP. In this situation, we are expecting “Green” color from Green class but we are getting “Blue” color from Green color. So the design of class is violating the LSP.
public class Green {
public void getColor() {
System.out.println("Green");
}
}
public class Blue extends Green {
public void getColor() {
System.out.println("Blue");
}
}
public class Main{
public static void main(String[] args) {
// violate LSP because color of green object is blue
Green green = new Blue();
green.getColor();
//output: Blue
}
}
In the main method, green object is asking for color but it’s getting “Blue” color. How to solve the issue. See the solution.
public interface IColor{
public void getColor();
}
public class Green implements IColor {
public void getColor() {
System.out.println("Green");
}
}
public class Blue implements IColor {
public void getColor() {
System.out.println("Blue");
}
}
public class Main{
public static void main(String[] args) {
IColor color = new Blue();
color.getColor();
//output: Blue
}
}
Example 2:
Here, “Fly()” and “walk()” are the actions of Bird class. Penguin inherits the Bird class but Penguin can not fly. As a child class Penguin can not use the fly. So design of class can not satisfy the LSP rules.
public interface Bird{
public void fly();
public void walk();
}
public class Parrot implements Bird{
public void fly(){ // to do}
public void walk(){ // to do }
}// ok
public class Penguin implements Bird{
public void fly(){ // to do }
public void walk(){ // to do }
} // it's break the principle of LSP. Penguin can not fly.
In the above example, object of Penguin can not access the fly method. . So we can solve the problem below way.
public interface Bird{
// to do;
}
public interface FlyingBird extends Bird{
public void fly(){}
}
public interface WalkingBird extends Bird{
public void walk(){}
}
public class Parrot implements FlyingBird, WalkingBird {
public void fly(){ // to do}
public void walk(){ // to do }
}
public class Penguin implements WalkingBird{
public void walk(){ // to do }
}
Benefits:
The benefit of following LSP is that it leads to more modular, extensible, and maintainable code. Here are some specific benefits:
- Code reusability: When LSP is followed, the code can be reused easily because it allows for the use of generic interfaces or base classes that can be implemented or extended by different subclasses.
- Improved code quality: By following LSP, the code becomes more understandable and easier to maintain.