This series is about Clean Code, SOLID principles, and all kinds of other cool stuff I talk about in my Clean Code classes.
The Rectangle and the Square Part IThe Rectangle and the Square Part II

In my Clean Code class, I go through this example about the Liskov Substitution Principle (LSP), part of the SOLID material. This example, the rectangle and the square, never fails to stump people, both experienced and less so.

It starts out with the question: Is a square a rectangle? Which of course, everybody knows is true.

I then show an example of a definition of a Rectangle class:

public class Rectangle { 
	protected int _width; 
	protected int _height;

	public int GetWidth() { return _width; 	} 
	public int GetHeight() { return _height;  } 
	public void SetWidth(int width) { _width = width; } 
	public void SetHeight(int height) { _height = height; } 
}

Then of a Square which inherits from Rectangle:

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

So far, so good. Clean code at its best.

Now let’s look at the client code, using the Rectangle class:

var shape = new Rectangle(); 
shape.SetWidth(2); 
shape.SetHeight(5); 
Console.WriteLine(String.Format(“Area = {0}”, 
	shape.GetHeight() * shape.GetWidth()));

Which, produces an area of 10. Then I go describe the same usage of a Square:

var shape = new Square(); 
shape.SetWidth(2); 
shape.SetHeight(5); 
Console.WriteLine(String.Format(“Area = {0}”, 
	shape.GetHeight() *	shape.GetWidth()));

Which of course produces 25. At this point, everybody understands the results, but something feels wrong. And they can’t really articulate why.

Then somebody says: “That’s not how you calculate the area of a square.”

Now we’re getting somewhere.
But the problem is still not clear, since the code is obviously correct. The design is ok. There is no bug. Clean code, we’ve already said.

What is the correct way to calculate the area of a square?

“Well, you set either the height or the width, not both”.

Which is different than calculating an area of a rectangle.

The Shape Of Things To Come

As I describe in the Clean Code class, LSP requires the ability to substitute the derived class (the Square in our case), with the base class (the Rectangle), and expect the same behavior. Obviously, this does not happen here, because the same behavior leads to a different result.

Here’s the kicker: Behavior doesn’t mean just implementation. And it is not just having the right interfaces.

Behavior also includes operations across methods. For the same inputs, and same method operations, we expect both the rectangle and the square to return the same results. They don’t and the result is different.

Big deal, so the code is not LSP-compliant. Is the world coming to an end?

No, it’s not. But.

It does mean that we’ll write more code to support the special cases, while we can be writing a more generic clean code. And we’ll need to unit test these other cases as well. Having different things means more code.

But there’s even bigger issue. We’ll discuss it next time.

Categories: Clean code

2 Comments

David V. Corbin · November 8, 2017 at 6:11 pm

100% spot on about the LSP and related. However I disagree with the protected fields and the implementation in the derived class as being “Clean code at its best.” (best meaning there is ZERO room for improvement… instead consider the following.

public override void SetWidth(int width) {
base.SetWidth(width);
base.SetHeight(width);
}

Now one is clean even if one extends the base class to have things like events on th changing of geometry.

    Gil Zilberfeld · November 12, 2017 at 12:52 pm

    David,

    Nice. It is indeed extensible, but it doesn’t solve the problem, because it is caused externally (how the client code uses it) and not internally. Part II (coming up this week if WP doesn’t mess with my automatic scheduling) actually talks about this. Stay tuned.

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *