a rule that guides proper inheritance.

First Thought by Barbara Liskov:

  1. Inventor of Abstract Data types
  2. Turing Award for her contribution in OOPs
  3. Her rules on Inheritance

All the programming languages can be rooted back to type theory.

What is a Type? A bag of operations, there maybe data with in it, But we mainly care about its operations.

Integer, String, List, etc…are all types in any programming languages, are just Classes in rear view.

Let’s take a look at Types and Sub Types with an example.

typedef point
{
	double x, y;
}

double distance(point* this, point* p2);
void translate(point* this, double dx, double dy);
void rotate(point* this, double angle);

Now this is a Point type.

typedef describedPoint{
	double x, y;
	char* description;
}

And this is a Sub Type.

According to LSP: all derived classes should be subtypes of base class type. In the above example Point is base class DescribedPoint is derived class.

And it follows, LSP because, anywhere in the program Point can be replaced by DescribedPoint.

==The 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.==

In the above diagram U should be able to use S(which is derived class of T), without breaking the expectations.

Sub Types are achieved In Static Languages using Inheritance In Dynamic Languages using Duck Typing or Inheritance

Both Type and Sub Type should have same methods, even if they have different implementations.

If LSP is violated for Inheritance or Duck Typing, it results in Refused Bequest.

Let’s look at an example where LSP violation causes Refused Bequest.

class Rectangle:
	def __init__(self, height, width):
		self.__height = height
		self.__width = width
	def set_height(self, height):
		self.height = height
	def set_width(self, width):
		self.width = width
	def get_height(self):
		return self.height
	def get_widht(self):
		return self.width
	def get_area(self):
	return self.__width * self.__height

Now, our Manager or Business requirement wants a Square type too.

Programmer, on knowing Square is an example of Rectangle. Without much thought he goes for inheritance between Rectangle and Square.

But this is a mistake. The first hiccup we get is the constructor Rectangle takes two params, where Square does not need two params. Ok, lets take same param twice, to cross this issue.

class Square(Rectangle):
	def __init__(self, side):
		super().__init__(side, side)
	def set_width(self, side):
		self.__width = side
		self.__height = side
	def set_height(self, side):
		self.__width = side
		self.__height = side
	

If we use this Square in real world. This will break.

Let’s say a program tries to set_width of Square.

For Square, it should set both sides.

But If someone tries to Use Square in Place of Rectangle. And tries to set_width. then instead of changing one dimension as expected in Rectangle. It changes two dimensions.

This will result unexpected behavior.

We can fix this by introducing the is_instance of in set_width of square class. But this will violate OCP. So if LSP occurs, if we try to fix it temporarily we may lead to OCP violation.

Solution:

Realize that Rectangle and Square shapes are type and subtype, the Representation of Rectangle(height, width), Square(side) are different.

And Representatives does not necessarily share type and subtypes.

One another similar example is Complex => Real => Integer This inheritance breaks too. As complex has two variables and others have only one variables.

This can be intuitively addressed by naming the classes appropriately. Instead of Rectangle => RectangleRepr

This way we will realise, we are establishing inheritance on Representation and not on rectangle itself.

How to know we are violating LSP:

  1. If base class does something, derived class should do the same, without missing the expectations of the callers => Subtypes can do more than type but not less.
  2. Presence of Is_Instance with else if conditions.