Liskov Substitution Principle (LSP)
The Liskov Substitution Principle (LSP) is a concept in object-oriented programming that states that if a program is using a base class, it should be able to use any of its subclasses without the program knowing it. In other words, subclasses should be substitutable for their base classes without affecting the correctness of the program.
The Liskov Substitution Principle is a key concept in object-oriented programming, and it can be explained with a simple analogy:
Think of electronic devices that use a standard USB charger. The principle is like saying that any device that charges with a USB cable should be able to use any USB charger. If you have a phone that charges with a USB cable, you should be able to charge it with any USB charger, not just the one that came with it.
Translating this to programming:
Substitutable Objects: If class B is a subclass of class A, then objects of class A should be able to be replaced with objects of class B without affecting the behavior of the program. This is similar to how any USB charger can be substituted for another.
Maintaining Behavior: Subclasses should not change the expected behavior when they replace instances of their parent class. If using a subclass object makes your program crash or behave incorrectly, then it violates this principle.
Consistency in Design: It encourages you to design classes so that a subclass can be used in place of its superclass without needing to know the difference. Just like how you can plug any USB device into a USB charger without worrying about whether it will work or not.
Example: Imagine a class
Bird
with a methodfly()
. If you have a subclassPenguin
(since penguins are birds), according to the Liskov Substitution Principle, wherever you useBird
, you should be able to usePenguin
without issue. However, penguins can't fly, so usingPenguin
in place ofBird
might cause problems if thefly()
method is needed.
The Liskov Substitution Principle in OOP emphasizes that subclasses should be replaceable for their parent classes without altering the correct functioning of the program, much like how different USB chargers can be used interchangeably with various USB devices.
The principle is named after Barbara Liskov, who introduced it during her 1987 keynote address titled "Data Abstraction and Hierarchy". This principle is also part of the five principles of SOLID in object-oriented design and programming:
The Liskov Substitution Principle is formally defined as: "If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T."
In simpler terms, this means that any method that uses a reference to the base class must be able to use any of its subclasses indistinguishably, without even knowing it, thus ensuring the same behavior. Violations of the LSP can often lead to strange and unpredictable behavior in software.
LSP is about creating subclasses that can truly stand in for their base classes. It's a specification of a behavioral notion of subtyping. Let's take a look at a more concrete example.
Assume we have a class named Rectangle
with methods setWidth(double width)
, setHeight(double height)
, getWidth()
, and getHeight()
. It also has a method getArea()
which returns the product of height and width. This makes perfect sense for a rectangle.
Now, consider we want to add a class Square
. A square is a special type of rectangle, so it makes sense to make Square
a subclass of Rectangle
. However, a square has a special property: its width is always equal to its height.
How would we implement setWidth()
and setHeight()
in Square
? If we were to use the superclass methods, we could set width and height independently, which would violate the rule of a square. So, we might decide to override setWidth()
and setHeight()
in Square
to always change both width and height to the new value.
But now consider a piece of code that uses a Rectangle
object and changes the width expecting the height to remain unchanged (which is perfectly reasonable for a rectangle). If we were to pass a Square
object into this code, it wouldn't behave correctly because setting the width also changes the height in a Square
. Thus, we've violated the Liskov Substitution Principle.
The Liskov Substitution Principle forces us to make sure all derived or child classes should be substitutable for their base or parent class. A violation of this principle can introduce bugs when we attempt to use a subclass object in a place where a superclass object is expected.
So in the example above, to adhere to the LSP, Square
should not be a subclass of Rectangle
. Instead, both Square
and Rectangle
could be subclasses of a more generic class, perhaps named Polygon
or Shape
.
In practical terms, following LSP allows us to add more and more subclasses into a software system without fear of breaking existing functionality. We can confidently use objects of the superclass and know that any object of a subclass can stand in and behave correctly.
COSC-1437 / ITSE-2457 Computer Science Dept. - Author: Dr. Kevin Roark