Recently I was watching a very nice explanation of LSP on Christopher Okhravi’s YouTube channel. After approximately five minutes of watching this video I wanted to know one thing: how can I actually break it?
Because, you know, breaking things when you learn gives you better understanding of how they work. Am I right?
Let’s start with a simple Adder class and a class which will inherit from it:
public class Adder | |
{ | |
public virtual int Add(int operand1, int operand2) | |
{ | |
return operand1 + operand2; | |
} | |
} | |
public class AdvancedAdder : Adder | |
{ | |
} |
The AdvancedAdder class does the same that Adder class does for now. It adds two numbers.
Now we need a UnitTest that will check if the Liskov’s Substitution Principle was violated. It’s done by next three classes:
public abstract class AdderUnitTestsBase | |
{ | |
protected static Adder Adder; | |
[DataTestMethod] | |
[DataRow(2, 2, 4)] | |
[DataRow(-1, 2, 1)] | |
[DataRow(2, -3, -1)] | |
[DataRow(0, 0, 0)] | |
public void Add_ReturnsCorrectResult( | |
int operand1, int operand2, int result) | |
{ | |
Assert.AreEqual(result, Adder.Add(operand1, operand2)); | |
} | |
} | |
[TestClass] | |
public class AdderUnitTests : AdderUnitTestsBase | |
{ | |
[ClassInitialize] | |
public static void ClassInit(TestContext context) | |
{ | |
Adder = new Adder(); | |
} | |
} | |
[TestClass] | |
public class AdvancedAdderUnitTests : AdderUnitTestsBase | |
{ | |
[ClassInitialize] | |
public static void ClassInit(TestContext context) | |
{ | |
Adder = new AdvancedAdder(); | |
} | |
} |
We are ready to break it!
Well to be honest I spent a half of an hour trying to figure it out.
Adding third parameter to make the AdvancedAdder sum three numbers didn’t break LSP. It just added another Add method with a different signature extending the class functionality.
Changing parameters type did not break it either. Again it just added one more method to the class.
public class AdvancedAdder : Adder | |
{ | |
public int Add(int operand1, int operand2, int operand3) | |
{ | |
return operand1 + operand2 + operand3; | |
} | |
public uint Add(uint operand1, uint operand2) | |
{ | |
return operand1 + operand2; | |
} | |
} |
Then I came up with an idea that I HAVE to override Add method to make it to do something absolutely not related to the Adder class. Subtracting for example:
public class AdvancedAdder : Adder | |
{ | |
public override int Add(int operand1, int operand2) | |
{ | |
return operand1 - operand2; | |
} | |
} |
I did it!
Summary
It’s not that simple to violate Liskov’s Substitute Principle without overriding a method. Can you do it without “override”?
P.S. When I wrote this post two years ago I was confused by LSP. You can probably see it in the code above.
After some consideration, I decided to keep this post as is. Nostalgy, you know