web-dev-qa-db-fra.com

Comment vérifier le principe de substitution de Liskov dans une hiérarchie d'héritage?

Inspiré par Ceci Réponse:

Le principe de substitution de Lucov nécessite que

  • Les conditions préalables ne peuvent être renforcées dans un sous-type.
  • Les postconditions ne peuvent pas être affaiblies dans un sous-type.
  • Les invariants du superype doivent être préservés dans un sous-type.
  • Contrainte d'histoire (la "règle d'histoire"). Les objets sont considérés comme modifiables uniquement à travers leurs méthodes (encapsulation). Étant donné que les sous-types peuvent introduire des méthodes qui ne sont pas présentes dans le Superype, l'introduction de ces méthodes peut permettre des modifications de l'état dans le sous-type qui ne sont pas autorisées dans le Superype. La contrainte d'histoire l'interdit.

J'espérais si quelqu'un posterait une hiérarchie de classe qui enfreint ces 4 points et comment les résoudre en conséquence.
[.____] Je cherche une explication élaborée à des fins éducatives sur la manière d'identifier chacun des 4 points de la hiérarchie et de la meilleure façon de le réparer.

Remarque:
[.____] J'espérais poster un échantillon de code pour que les gens puissent travailler, mais la question elle-même est de savoir comment identifier les hiérarchies défectueuses :)

14
Songo

C'est beaucoup plus simple que cette citation le rend sonore, précis tel qu'il est.

Lorsque vous regardez une hiérarchie d'héritage, imaginez une méthode qui reçoit un objet de la classe de base. Demandez-vous maintenant, y a-t-il des hypothèses que quelqu'un édition de cette méthode pourrait faire ce qui serait invalide pour cette classe.

Par exemple ( à l'origine sur le site de l'oncle Bob ):

public class Square : Rectangle
{
    public Square(double width) : base(width, width)
    {
    }

    public override double Width
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Width;
        }
    }

    public override double Height
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Height;
        }
    }
}

Semble assez juste, non? J'ai créé un type de rectangle spécialisé appelé Square, qui maintient cette largeur doit être égale à tout moment. Un carré est un rectangle, il convient donc avec OO Principes, n'est-ce pas?

Mais attendez, que si quelqu'un écrit maintenant cette méthode:

public void Enlarge(Rectangle rect, double factor)
{
    rect.Width *= factor;
    rect.Height *= factor;
}

Pas cool. Mais il n'y a aucune raison que l'auteur de cette méthode aurait dû savoir qu'il pourrait y avoir un problème potentiel.

Chaque fois que vous dérivez une classe d'une autre, pensez à la classe de base et à ce que les gens pourraient supposer à ce sujet (comme "il a une largeur et une hauteur et ils seront tous deux indépendants"). Alors pensez "ces hypothèses restent-elles valables dans ma sous-classe?" Sinon, repensez votre conception.

17
pdr