DEV Community

Cover image for Liskov Substitution Principle
Maksim Ivanov
Maksim Ivanov

Posted on • Updated on

Liskov Substitution Principle

Originally posted on maksimivanov.com

In 1988 Barbara Liskov wrote something that now stands for L in SOLID principles. Let's dive in and learn what is it and how does it relate to TDD.

Here is the original formulation: "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."

Simply speaking: "Derived class objects must be substitutable for the base class objects. That means objects of the derived class must behave in a manner consistent with the promises made in the base class contract."

Speaking even more simply: "Derived class objects should complement, not substitute base class behavior."

liskov example

LSP can also be described as a counter-example of Duck Test: "If it looks like a duck, quacks like a duck, but needs batteries – you probably have the wrong abstraction"

So, In Real World

If you have some class Foo and a derived class SubFoo, then if you change all the notions of Foo class to SubFoo – the program execution shouldn't change, as SubFoo dosen't change the Foo class functionality, and only extends it.

Let's See The Example

Getting back to ducks. Let's describe a Duck. We have very low expectations on it. We only expect it to be able to quack and nothing else.

describe('Duck', function(){
  describe('#quack', function(){
    it('produces "Quack" sound', function(){
      const duck = new Duck();
      expect(duck.quack()).toEqual('Quack');
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

Fine, now lets define the basic duck.

class Duck{
  constructor(){
    // Duck initialization process
  }

  quack(){
    return 'Quack';
  }
}
Enter fullscreen mode Exit fullscreen mode

We run the spec and it passes. Cool, now let's create a derived class MechanicalDuck. It should also be able to quack. The only difference is that it needs batteries to operate.

class MechanicalDuck extends Duck{
  constructor(battery=null){
    super();
    this._battery = battery;
  }

  quack(){
    if(!this._battery){
      throw 'Need battery to operate.';
    }
    return 'Quack';
  }
}
Enter fullscreen mode Exit fullscreen mode

Now according to LSP, we should be able to safely change instances of base class to instances of derived class. Let's change our spec a bit and try to use MechanicalDuck instead of Duck.

Uh-oh, test failed. MechanicalDuck needs battery to quack. So MechanicalDuck here is clearly not a duck. Even though it's interface might look similar, it's behavior is totally different.

But What Would Be A Proper Subclass?

In our case it might be a FemaleDuck. Let's implement it.

class FemaleDuck extends Duck{
  constructor(){
    super();
    // Initialization of female stuff
    this._butt = new FemaleDuckButt();
  }

  layAnEgg(){
    const egg = this._butt.layAnEgg();
    return egg;
  } 
}
Enter fullscreen mode Exit fullscreen mode

FemaleDuck will successfully pass the duck test, as we didn't change the behavior, but only extended it. Our duck can lay eggs, hurray!

Top comments (6)

Collapse
 
rafalpienkowski profile image
Rafal Pienkowski

Very nice article.

In my opinion examples and code snippets are very useful for developers
to understand thing (especially principles, design patterns etc.). Great that you added them.

I'm waiting for your posts describing others letters from SOLID acronym.

Collapse
 
satansdeer profile image
Maksim Ivanov

Thanks, that's exactly what I'm going to do :-)

Collapse
 
rafalpienkowski profile image
Rafal Pienkowski

I'm really looking forward to it. Thanks.

Collapse
 
enriquemorenotent profile image
Enrique Moreno Tent

Best article I have read in a long while. Congrats on the very clear explanation.

Collapse
 
halas profile image
Piotr Hałasiewicz

Wow, great explanation - I finally really feel I understand it :)

Collapse
 
satansdeer profile image
Maksim Ivanov

Thanks! So great to hear. Really motivates to write more :-)