The Liskov Substitution Principle (LSP) states that:
Subtypes should be substitutable for their base types. This means that any instance of a subtype should be able to be used in place of its base type without affecting the correctness of the program.
In other words, if class S is a subclass of class T, then an object of class T should be able to be replaced with an object of class S without affecting the program's properties.
Violation of the principle
Consider the following example:
public class FlyingAnimal
{
public virtual void Fly()
{
Console.WriteLine("I am a flying animal");
}
}
public class Penguin : FlyingAnimal
{
public override void Fly()
{
throw new NotSupportedException("Penguins cannot fly");
}
}
In this example, the Penguin
class inherits from the FlyingAnimal
class. However, since penguins cannot fly, the Fly
method in the Penguin
class throws an exception. This violates the LSP, as replacing an object of the FlyingAnimal
class with an object of the Penguin
class could cause runtime errors.
Application of the principle
To resolve the violation of the LSP, we can use the following design:
public abstract class Animal
{
public virtual void Behavior()
{
Console.WriteLine("I am an animal");
}
}
public class FlyingAnimal : Animal
{
public override void Behavior()
{
base.Behavior();
Console.WriteLine("and I can fly");
}
}
public class Penguin : Animal
{
public override void Behavior()
{
base.Behavior();
Console.WriteLine("but I cannot fly");
}
}
In this example, we have introduced an abstract class Animal
and two derived classes: FlyingAnimal
and Penguin
. The Animal
class has a Behavior
method that describes the basic properties of an animal. The derived classes extend the Behavior
method to describe additional specific properties for each type of animal. In this way, we can replace an object of the Animal
class with an object of its subclasses without affecting the program's properties.
Conclusion
In this article, we have discussed the Liskov Substitution Principle, which teaches us to develop derived classes that can seamlessly replace their base classes without compromising the correctness of the program. This is achieved by using proper inheritance hierarchies and polymorphism, ensuring that derived classes adhere to the contracts set by their base classes. Consequently, this principal aids in creating code that is more maintainable and extensible, as it minimizes unexpected behavior when using derived classes in place of their base classes.
Top comments (0)