Circle–ellipse problem

The problem concerns which subtyping or inheritance relationship should exist between classes which represent circles and ellipses (or, similarly, squares and rectangles).

More generally, the problem illustrates the difficulties which can occur when a base class contains methods which mutate an object in a manner which may invalidate a (stronger) invariant found in a derived class, causing the Liskov substitution principle to be violated.

It may be possible to avoid this if the language (such as Eiffel) makes constant values of a class, functions without arguments, and data members interchangeable.

This is in general good practice, but may require that the original author of Ellipse anticipated such a problem, and defined the mutators as returning a value.

Eventually, it is only a clever way to bypass the Liskov constraint by stating up-front that the post condition may or may not be valid.

Some languages preclude this type of change, and others impose restrictions on the Ellipse class to be an acceptable replacement for Circle.

For languages that allow implicit conversion like C++, this may only be a partial solution solving the problem on call-by-copy, but not on call-by-reference.

One can change the model so that instances of the classes represent constant values (i.e., they are immutable).

This means that it is no longer a problem to define Circle.stretchX, and the inheritance reflects the mathematical relationship between circles and ellipses.

A disadvantage is that changing the value of an instance then requires an assignment, which is inconvenient and prone to programming errors, e.g., Orbit(planet[i]) := Orbit(planet[i]).stretchX A second disadvantage is that such an assignment conceptually involves a temporary value, which could reduce performance and be difficult to optimise.

This has a disadvantage of introducing an extra class where all that is desired is specify that Circle does not inherit modifiers from Ellipse.

Also, one may provide conversion methods like Circle.asEllipse, which returns a mutable Ellipse object initialized using the circle's radius.

[1] This problem has straightforward solutions in a sufficiently powerful OO programming system.

If these two pieces of information, which are ultimately only bits in the machine, are kept synchronized so that they say the same thing, everything is fine.

It is clear that a circle cannot satisfy the invariants required of it while its base ellipse methods allow mutation of parameters.

The circle and ellipse models are deliberately simplified to avoid distracting details which are not relevant to the circle–ellipse problem.

If that were the case, then the idea that class Prisoner extends FreePerson is clearly wrong.

However, stating that a prisoner can not move an arbitrary distance in any direction and a person can is a wrong premise once again.

Now, when implementing walkToDirection for the subclass Prisoner, you can check the boundaries and return proper walk results.