What is the Liskov Substitution Principle?
“if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program.”
All this really means is that objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
In dynamic languages like Ruby, the Liskov principle (LSP) works slightly differently because Ruby less rigidly enforces how types work (so-called “Duck Typing”) as opposed to a language like Java, where type safety is enforced by the compiler. This means that LSP winds up applying more to which messages an object responds to, and less its type.
To illustrate the principle, think about 2 classes, Rectangle and Square. Idiomatically, Rectangle is a superclass of Square. Rectangle and Square both have width and height attributes, but when setting the width of the Rectangle and the width of the Square, different behaviors are applied.
class Rectangle attr_accessor :width, :height end class Square attr_accessor :side def height=(height) @side=height end def width=(width) @side=width end def width() @side end def height() @side end end
Applying the Liskov Substitution Principle, we can say that since we would consider Square a subtype of Rectangle, any function that acts on a Rectangle should work with both Squares and Rectangles. But the following code would fail:
shapes = [Square.new, Rectangle.new] def setRectangleWidthHeight(shapes) shapes.each do | shape | shape.width = 4 shape.height = 5 assert(true, shape.width * shape.height) #fails for the Square! LSP Violation! "Refused Bequest" end end
What can done to avoid this violation, which is also called a “Refused Bequest” by the SOLID gurus?
One idea might be to make direct instances of Square impossible by changing Square to a module.
Another option might be to reconsider the notion of Square inheriting from Rectangle in the first place! To use Bob Martin’s explanation from his codecast:
The problem is this is not a Rectangle. It’s a piece of code! It’s a representation of a Rectangle. Representatives do not share the relationships of the things they represent. For example, imagine 2 people who are getting divorced. Each one of them has a lawyer who represents them. Its very unlikely those 2 lawyers themselves are getting divorces because the reresentatives of things do not share the relationships of the things they represent!
Objects in a program are not objects, they are Representations of objects!
If you’d like to learn more about LSP, check out the following resources:
- Liskov Substitution Principle and the Ruby Core Libraries
Image credit: Kenneth C. Zirkel