│  client      │                                     
    │              │                                     
    │              │                           x         
    │       ┌──────┘          ┌──────┐        xxx        
    │       │                 │      │       xx xx       
    │   ┌───┘             ┌───┘      └───   xx   xx      
    │   │                 │ base class     xx     xx     
    │   │                 │               xxxxxxxxxxx     
    │   └───┐             └───┐      ┌─── xx       xx    
    │       │                 │      │    xx       xx    
    │       └──────┐          └──────┘                   
    │              │                                     
    │              │                                     
    │              │                      xxxxxx         
    └──────────────┘          ┌──────┐    x     x       
                              │      │    xx     x       
                          ┌───┘      └─── x      x       
                          │ derived       x     x    
                          │ class 1       xxxxxxx       
                          └───┐      ┌─── x      x      
                              │      │    x       x      
                              └──────┘    xx     x      
                                          x     x      
                              ┌──────┐      xxx        
                              │      │    xx             
                          ┌───┘      └─── x              
                          │ derived       x              
                          │ class 2       x              
                          └───┐      ┌─── xx             
                              │      │     xx         
                              └──────┘       xxx       

Hello! Welcome back! Today, we’ll discuss the benefits of a really cool ‘good software engineering principle’ called the Liskov Substitution Principle (LSP).


Liskov Substitution Principle states that a derived object should be substitutable for its base object, without affecting correctness of the system. This means that anywhere you can use a base object, you should be able to use a derived object instead and the system should behave correctly.

This seems obvious, but let’s think about it a little. Have you ever seen a derived class that overrides a base method and then throws a NotImplementedException lol? Or a derived class that overrides a base method and then does something completely different than what the base contract specifies?

In both of these cases, the derived class is not substitutable for the base class. In the former case, you crash the system, and in the latter case, you get unexpected behavior. This is a violation of LSP.

What do I get? Why should I waste my time?

When you violate LSP, your system is less modular/flexible, because you can’t simply swap out behavior by swapping objects, since there is a risk that the new object will not abide by the contract set forth by the base object! Aye caramba!

On the other hand, if the system does follow LSP, you can go crazy with changing behavior by swapping objects, and the system will still behave correctly. Sure it may behave differently, but that is the whole point of swapping out objects, to get a new (but still correct) behavior!

Anyways, you’re smart, you can clearly see the modularity and flexibility you get by following LSP! You also get reliability and resilience in a way, because your system is more resilient to changes (that involve swapping out objects).

This includes interfaces!

You may think you get it, but I’m not done yet!

This concept applies to interfaces too, not just base classes! When you implement an interface, you better oblige by the contract set forth! And no, simply obliging by the function signature is not enough, you have to behaviorally oblige. This way, your implementation can be used anywhere the interface is used. Again, you get more flexibility/modularity/reliability.


Let’s end with a summary for the lazy folks Among Us (please don’t sue me!):

Liskov Substitution Principle is a ‘good software engineering principle’ that demands your derived objects behave correctly when substituted for base objects. This leads to more flexibility and modularity in your system, because you can swap out objects in order to swap behavior, and the system will still behave correctly. By still behaving correctly even when behaviors/objects are swapped, you’re technically getting resilience and reliability as well! What a bargain!

Also, remember, this applies to interfaces too! Oblige by the contract the interface sets forth!!

Anyways, hope you have an awesome day!