Saturday, April 19, 2008

Liskov Substitution Principle

The principle states: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. While checking for specific types is technically a violation of LSP, it's more commonly thought of as an OCP violation. LSP is more focused on the behavior of the specific instance itself, rather than types operating upon the specific instance. Below is an example of some code breaking this principle:

Interface: ICounter
1
2
3
4
5
6
7
8
9
10
namespace LSP
{
    public interface ICounter
    {
        void InitialCount(int count);
        void Increase();
        void Decrease();
        int Count { get; }
    }
}
Class: GoodCounter : ICounter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace LSP
{
    public class GoodCounter : ICounter
    {
        private int _Count;

        public GoodCounter()
        {
            _Count = 0;
        }

        public virtual void InitialCount(int count)
        {
            _Count = count;
        }
        public void Increase()
        {
            _Count++;
        }
        public void Decrease()
        {
            _Count--;
        }

        public int Count
        {
            get { return (_Count); }
        }
    }
}
Class: BadCounter : GoodCounter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace LSP
{
    public class BadCounter : GoodCounter
    {
        private int _Count;

        public BadCounter()
        {
            _Count = 10;
        }

        public override void InitialCount(int count)
        {
            _Count = 0;
        }
    }
}
The above example is violating the Liskov Substitution Principle because the BadCounter class is different in behavior than the GoodCounter class in two points: one the constructor is different and two the InitialCount method is different. These types of violations are easily discovered when having proper unit tests in place. Take a look at the unit tests that would demonstrate the violation using NUnit.
Class: CalculatorTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace LSP
{
    [TestFixture]
    public class CalculatorTest
    {
        [Test]
        public void GoodCounterConstructor()
        {
            ICounter counter = new GoodCounter();
            Assert.That(counter.Count, Is.EqualTo(0));
        }
        [Test]
        public void GoodCounterSetInitialCount()
        {
            ICounter counter = new GoodCounter();
            counter.InitialCount(10);
            Assert.That(counter.Count, Is.EqualTo(10));
        }
        [Test]
        public void GoodCounterSetIncreaseDecrease()
        {
            ICounter counter = new GoodCounter();
            counter.Increase();
            counter.Increase();
            counter.Increase();
            counter.Decrease();
            Assert.That(counter.Count, Is.EqualTo(2));
        }
    }
}
When running the unit test on the GoodCounter class everything works fine and we get an al green light, but when we would execute these test with the BadCounter than all tests would fail, thus indicating a violation of the Liskov Substitution Principle.

I hope you found these examples helpful. Also take a look at the following links:

http://www.objectmentor.com/resources/articles/lsp.pdf
http://www.lostechies.com/blogs/chad_myers/archive/2008/03/09/ptom-the-liskov-substitution-principle.aspx
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
http://www.nunit.org/index.php

No comments: