SOLID Design Principles

Liskov Substitution Principle (LSP)

The Liskov Substitution Principle states that objects of a superclass should be replaceable with objects of its subclasses without breaking the application.

LSP enforces something discussed so far: public inheritance should model an is-a relationship.

If class B inherits from class A: we can use pointers / references to B objects in the place of pointers / references to A objects: C++ gives us this.

Liskov Substitution is stronger: not only can we perform the substitution, but we should be able to do so at any place in the program, without affecting its correctness.

So precisely:

• If an invariance is true for class A, then it should also be true for class B
• If an invariant is true for method A::f, and B overrides f, then this invariant must be true for B::f
• If B::f overrides A::f
• If A::f has a precondition P and a postcondition Q, then B::f should have a precondition P' such that P=>P' and a postcondition Q' such that Q'=>Q
• B::f needs a weaker precondition and a stronger postcondition

Ex: Contravariance problem

• Happens whenever we have a binary operator where the other parameter is the same type as *this.

As we’ve seen before, we must take in the same parameter when overriding. C++ enforces this, which actually enforces LSP for us.

1. A Circle is-a Shape
2. A Shape can be compared with other Shapes
3. A Circle can be compared with any other Shape

To satisfy LSP, we must support comparison between different types of Shapes.

 typeidreturnsstd::type_info objects that can be compared.

dynamic_cast: Is other a Circle, or a subclass of Circle? typeid: Is other exactly a Circle?

typeid uses the dynamic-type so long as the class has at least one virtual method.

Example of Violation of LSP

Ex: Is a Square a Rectangle? 4th Grader: Yes, a Square is a rectangle.

A square has all the properties of a rectangle.

What is the issue here?

we expect postcondition for Rectangle::setWith to be that the width is set and nothing else changes. But this is violated by our Square` class.

Violates LSP, does not satisfy an is-a relationship. Conclusion: Square are not Rectangles.

Possibility: Restructure inheritance hierarchy to make sure LSP is respected.

In general: how can we prevent LSP violations?

We should restrain what our subclasses can do, only allows them to customize what is truly necessary.

We can use a design pattern: Template Method Pattern, to make this process easier.