The Liskov Substitution Principle - Agile Software Development Principles Patterns and Practices

For the past few weeks I have been doing a chapter-by-chapter review of Agile Software Development Principles, Patterns, and Practices by Robert Martin.  So far, it has covered a lot of good introductory information on various Agile Methodologies (Test-Driven Development, Refactoring, Pair Programming, Project Planning, Extreme Programming, etc.) as well as talked about various object-oriented programming principles, which is what I am still covering now.

Here is a chapter-by-chapter breakdown on what I have covered so far:

Agile Software Development, Principles, Patterns, and Practices

Book: Agile Software Development, Principles, Patterns, and Practices (Amazon)
Author: Robert C. Martin (Amazon)
Publisher: Prentice Hall; 1st edition (October 15, 2002)
Hardcover: 552 pages

Previous Chapters

 Chapter 10 is on the Liskov Substitution Principle:

 

Liskov Substitution Principle

“What is wanted is something like the following substitution property: 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. [Liskov88]“

 

Is it me, or is that painful to read?  Robert Martin puts it a bit simpler:

Subtypes must be substitutable for their base types

The classic example of this principle in code is inheriting the Square Class from the Rectangle Class.

 

Classic Example of LSP
public class Rectangle
{
    protected int _width;
    protected int _height;
    
    public int Width
    {
        get { return _width; }
    }
    
    public int Height
    {
        get { return _height; }
    }
    
    public virtual void SetWidth(int width)
    {
        _width = width;
    }
    
    public virtual void SetHeight(int height)
    {
        _height = height;
    }
}

public class Square: Rectangle
{
    public override void SetWidth(int width)
    {
        _width = width;
        _height = width;
    }
    
    public override void SetHeight(int height)
    {
        _height = height;
        _width = _height;
    }
}

[TestFixture]
public class RectangleTests
{
    [Test]
    public void AreaOfRectangle()
    {
        Rectangle r = new Square();
        
        r.Width = 5;
        r.Height = 2;
        
        // Will Fail - r is a square and sets
        // width and height equal to each other.
        Assert.IsEqual(r.Width * r.Height,10);
    }
}

 

If you look at the test above, it will fail because a square is being substituted for a rectangle and the area won't be 10 as expected.  It will actually be 4 because “unexpectedly” in this case, both height and width are being set to each other when the width or height is set on a square.  Therefore, if this behavior by Square is unacceptable and unexpected, Square should not be derived from Rectangle (at least not coded like this with these expectations anyway).

This is the whole point of the Liskov Substitution Principle.  It basically wants you to think clearly about the expected behavior and expectations of a class before you derive new classes from it.  It could turn out that when subtypes are substituted for a parent, you may get unexpected results.  This is where unit tests can really be handy.  The unit tests essentially describe and test for the expected behavior of objects (design by contract, if you will).

If you want to read some discussions as to the usefulness of this principle, whether it should be a principle, and thoughts on the classic example above, check out this wiki.  You can also read what Robert Martin has to say about it from this PDF.

posted on Friday, June 10, 2005 10:53 AM

Main

David Hayden Google +

David Hayden Twitter

Health & Fitness

JavaScript Patterns Book Review

HTML 5 and CSS3 - Develop with Tomorrow's Standards Today

Professional ASP.NET Design Patterns Book Review

Beginning Mac Programming Book Review

C# in Depth Book Review

ASP.NET MVC

Orchard CMS

Categories