DevExpress Newsletter 42: Message from the CTO

ctodx
08 February 2011

For this message, I’m continuing my discussion of the SOLID principles with L.

The L in SOLID - The Liskov Substitution Principle

Let's continue our discussion of the SOLID principles. Today we'll look at the L in SOLID: the Liskov Substitution Principle.

This one is simple in the extreme: Subtypes must be substitutable for their base types. Named after Barbara Liskov who first formulated it in 1988, it's the principle at the heart of any class inheritance chain. If you write a function or a class that uses a parameter or variable of some base class then you should be able to pass an instance of some descendant class and the behavior should remain exactly the same.

Unfortunately I don't have any examples of this from my own code since I have long since embraced this principle, so I'll have to make something up. Suppose we have a base class called Bird that has a virtual method called Fly.

abstract class Bird {
  abstract void Fly();
}

Then you could write a descendant Duck class that overrode that method to do something pertaining to how ducks fly.

class Duck : Bird {
  override void Fly() {
    Quack();
    FlapWings();
    Jump();
    ...
  }
}

Everywhere that expected an instance of Bird could be passed an instance of Duck and everything would work just fine.

And then someone wrote an Ostrich class.

class Ostrich : Bird {
  override void Fly() {
    throw new NotImplementedException();
  }
}

The problem here is that, although an Ostrich instance is substitutable for a Bird instance, it introduces some new breaking change to every place a Bird is asked to Fly: an exception is thrown because of course ostriches don't fly. This descendant class breaks the LSP: you are introducing new behavior into existing code by writing a descendant of the base class.

Now, sure you can go back and make some modifications to your Bird class to take account of this new functionality, but consider this: in doing so you are violating the open-closed principle, OCP. That is, classes should be open for extension but closed to modification.

As you can see: the LSP is closely linked to the OCP. If you follow LCP, your class hierarchy will follow OCP implicitly. You are creating base classes that can be extended ad nauseam by writing descendant classes, all the time ensuring that you don't have to modify the code of the base classes.

You can watch the video here. (Now updated.)

The problem with this principle is that everyone assumes that everyone else knows and applies it as well as they do. And yet I continue to see examples where the principle is violated. Possibly the best (or should that be worst?) example I remember is the one where the class hierarchy was something like BaseClass, FirstDescendant, DescendantOfFirst, with some virtual method X. In DescendantOfFirst.X, the developer wanted to skip calling the overridden version in FirstDescendant, and go directly to BaseClass.X. Brrr.

The same principle applies with duck typing in languages that don’t really have a class model. Here the function being called assumes that an object being passed to it has some well-defined methods. Objects that implement these methods should be substitutable for the object prototype the function expects. For instance, in Java Script:

var flyAway = function(animal) {
  animal.fly();
};

var flyingSquirrel = {
  ...
  fly: function() {
    leap();
    glide();
  }
};

flyAway(flyingSquirrel);

As before, the whole series is based on papers written by Uncle Bob (Robert C Martin). Click here for the PDF on LSP.

(I’ll note that this avian example for the Liskov Substitution Principle came from Bertrand Meyer’s seminal Object-Oriented Software Construction.)

4 comment(s)
Joachim Meyer
Joachim Meyer

wrong link: "You can watch the video here." leads to the PDF on LSP

9 February, 2011
CESAR F. QüEB
CESAR F. QüEB

Many thanks Julian,

This theory is really appreciated.

Best practices!

Regards

9 February, 2011
Julian Bucknall (DevExpress)
Julian Bucknall (DevExpress)

Joachim: Thanks for the warning (a copy/paste fail); it's now fixed.

Cheers, Julian

9 February, 2011
Regan Campbell
Regan Campbell

My question is:

How does one then deal with design "flaws" in base classes (as per the avian example), given OCP?

Forgive my ignorance (and please be gentle ;)

9 February, 2011

Please login or register to post comments.