Je crois que le raisonnement est à peu près le suivant :
Disons que vous avez une méthode qui accepte un rectangle et ajuste sa largeur :
public void SetWidth(Rectangle rect, int width)
{
rect.Width = width;
}
Il devrait être parfaitement raisonnable, compte tenu de ce qu'est un rectangle, de supposer que ce test sera réussi :
Rectangle rect = new Rectangle(50, 20); // width, height
SetWidth(rect, 100);
Assert.AreEqual(20, rect.Height);
... car la modification de la largeur d'un rectangle n'affecte pas sa hauteur.
Cependant, disons que vous avez dérivé une nouvelle classe Carré de Rectangle. Par définition, un carré a une hauteur et une largeur toujours égales. Essayons à nouveau ce test :
Rectangle rect = new Square(20); // both width and height
SetWidth(rect, 100);
Assert.AreEqual(20, rect.Height);
Ce test échouera, car le fait de fixer la largeur d'un carré à 100 modifiera également sa hauteur.
Ainsi, le principe de substitution de Liskov est violé en dérivant le Carré du Rectangle.
La règle "is-a" a du sens dans le "monde réel" (un carré est bien une sorte de rectangle), mais pas toujours dans le monde de la conception de logiciels.
Modifier
Pour répondre à votre question, la conception correcte devrait probablement être que le rectangle et le carré dérivent tous deux d'une classe commune "Polygone" ou "Forme", qui n'applique aucune règle concernant la largeur ou la hauteur.