IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Redéfinition de méthode et principe de Liskov


Sujet :

C++

  1. #21
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    C'est autre chose, et n'a plus à voir avec la programmation par contrats ;-). Cela dit, tu ne vérifies pas dans l'appelant ton contrat, tu appelles sans rien vérifier, et tu laisses l'appelé faire du defensive programming et vérifier que tu l'as appelé correctement. Ça marche parce que tu le fais bien, mais que se passe-t-il si une classe E dérivant de A décidait, parce qu'elle en a envie, de ne plus lancer BadRange, mais InvalidParameterValue ?
    à ce moment là, il est de ton ressort d'avoir une politique d'exception cohérente

    Tu veux qu'une classe dérivée puisse lancer une exception différente pas de problème: met au point une hiérarchie d'exception qui peut apparaitre sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    /* une exception "générique" */
    class MyException : public std::exception
    {
        public:
            MyException(std::string const & m):std::exception(),msg(m)
            {
            }
            ~MyException() throw(){}
            const char* what() const throw(){return msg.c_str();}
        private:
            std::string msg;
    };
    /* tes exceptions réelles, qui ne font que réaliser l'exception de base */
    class BadRange : public MyException
    {
        public:
            BadRange():MyException("Valeur hors des limites permises"){}
    };
    class InvalidParameterValue :public MyException
    {
        public:
            InvalidParameterValue ():MyException("valeur de parametre invalide")
            {
            }
    };
    et attrape l'exception de base sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    void g(A& param, int val)
    {
        try
        {
            param.f(val);
        }
        catch(MyException &e)
        {
            /* gestion de l'exception */
        }
    }
    Là je ne te comprends pas du tout. Où est la notion de "substitution", si tu enlèves le polymorphisme ?
    il ne faut pas confondre le polymorphisme et les comportements polymorphes...

    Le polymorphisme peut tout à fait s'appliquer sur une série de comportements polymorphes, mais d'autres comportements peuvent rester parfaitement stables et non polymorphes

    Le principe de liskov s'applique sur ces comportements qui n'ont pas pour vocation d'être réimplémentés dans les classes dérivées
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  2. #22
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le principe de liskov s'applique sur ces comportements qui n'ont pas pour vocation d'être réimplémentés dans les classes dérivées
    Typiquement, les invariants, non?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #23
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Typiquement, les invariants, non?
    Oui, je ne revenais plus sur le terme
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #24
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Je vais te donner un exemple, pour te permettre de comprendre...

    Soit une classe Base qui sert de base polymorphe, et se présentant comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Base
    {
        public:
            /* un comportement quelconque polymorphe */
            virtual void foo();
            /* un autre comportement invariant, n'ayant aucune vocation à
             * être redéfini
             */
            void bar();
            /* le reste de la classe */
    };
    Soit un classe Derivee qui hérite de la classe Base, et qui redéfini le comportement polymorphe foo, se présentant comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Derivee : public Base
    {
        public:
            virtual void foo();
            /* le reste de la classe */
    };
    foo est un comportement polymorphe, ce qui fait qu'il doit s'adapter au type réel de l'objet...

    Il est normal que le principe de substitution de liskov ne s'applique pas à ce comportement, vu qu'il s'adapte justement au type réel de l'objet en cours d'utilisation

    Par contre, si le comportement non polymorphe bar est vrai pour un objet de type Base, il doit le rester pour un objet de type Derivee.

    Si tu ne peux pas dire que le comportement induit par bar (qui n'a, rappelons-le pas vocation à être redéfini) appliqué à un objet de type Derivee reste vrai et cohérent, tu ne respecte pas le principe de liskov, et uniquement sur ce comportement particulier, qu'il s'agisse d'une méthode constante (telle que size() ou autre) ou non
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #25
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Du coup, ça veut dire que pour un comportement polymorphe, il est presque cohérent de restreindre les pré-conditions. Pour un comportement non polymorphe ça revient à modifier le type qu'est censé représenté la classe de base (au sens où c'est défini ici). D'où l'exigence "Les préconditions ne peuvent pas être renforcées dans une sous-classe."
    J'ai rien compris ?

  6. #26
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Du coup, ça veut dire que pour un comportement polymorphe, il est presque cohérent de restreindre les pré-conditions.
    à les restreindre, ou à les étendre... Le tout dépendant du sens donné à l'héritage (généralisation ou spécialisation )
    Pour un comportement non polymorphe ça revient à modifier le type qu'est censé représenté la classe de base (au sens où c'est défini ici). D'où l'exigence "Les préconditions ne peuvent pas être renforcées dans une sous-classe."
    J'ai rien compris ?
    Ni renforcées ni étendues (qu'il s'agisse de pré ou de post conditions)...Si la condition n'est pas polymorphe, elle doit être vraie aussi bien pour un objet dont le type réelle est la classe de base que pour n'importe quel objet dont le type est dérivé
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #27
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Ni renforcées ni étendues (qu'il s'agisse de pré ou de post conditions)
    Cela me parait bizarre, car contraire à la covariance et la contravariance.

    Selon ces principes, une classe dérivée est en droit d'étendre les pré-conditions (contravariance pour les paramètres, non-possible par C++) et de restreindre les post-conditions (covariance pour la valeur de retour, autorisée en C++).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #28
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    à ce moment là, il est de ton ressort d'avoir une politique d'exception cohérente
    Problème, rien dans le langage ne me le garantit. Et la pratique tend à prouver que ce qui n'est pas garanti, est faux.

    Tu veux qu'une classe dérivée puisse lancer une exception différente
    Non, justement, je veux l'empêcher !

    Mon but, c'est que si je suis sûr qu'appeler f() sur un A va réussir, alors, appeler f() sur un B va réussir aussi. La programmation par contrat me garantit ça, car elle garantit que B::f demande au plus que les préconditions de A::f soient respectées.

    Il est normal que le principe de substitution de liskov ne s'applique pas à ce comportement, vu qu'il s'adapte justement au type réel de l'objet en cours d'utilisation
    Je crois que si tu penses ça, tu passes à côté du but du LSP. Ou alors, c'est moi qui n'ai rien compris au LSP .

    Quand je code une fonction qui prend un A& en paramètre, je n'ai aucune idée du type réel qui m'est passé. Je sais juste qu'il est A ou dérive de A. Je peux donc vérifier les contrats définis par A, pas plus. Si on me passe un B, et qu'il vérifie les contrats de A, pas de problème. Mon algorithme va fonctionner. Si on me passe un C, qui ne vérifie pas les contrats de A, mais est plus restrictif, ça risque fortement de merder.

    Le LSP me dit que, si je peux substituer un C à un A, alors, tout algorithme qui fonctionnait pour un A, fonctionne pour un C. Ça s'applique complètement aux comportements polymorphes, du moment qu'on respecte le contrat !

    Un héritage qui restreindrait les préconditions, rend faux tout algorithme qui se basait sur ces préconditions. Un héritage qui étend ces préconditions, lui, n'empêche pas ces algorithmes de fonctionner (c'est l'inverse pour les postconditions).

    Si la condition n'est pas polymorphe, elle doit être vraie aussi bien pour un objet dont le type réelle est la classe de base que pour n'importe quel objet dont le type est dérivé
    Une condition non-polymorphe, c'est une restriction inutile. Le tout est de savoir dans quel sens tu as le droit de modifier ta condition :
    - les préconditions peuvent être étendues (ce n'est pas parce que A::f n'accepte pas quelque chose, que B::f n'a pas le droit de l'accepter)
    - les postconditions peuvent être restreintes (si A::f garantit quelque chose, B::f a le droit de garantir quelque chose de plus)
    - les invariants peuvent être restreints (comme les postconditions)

    Une dernière chose. On dit souvent qu'un des "problèmes" de la programmation par contrat, c'est que B::f() inclut une précondition évidente : "l'appelant est un B", et que cette précondition est plus restrictive que celle de A::f() qui est "l'appelant est un A". C'est effectivement vrai, mais c'est un mauvais argument, dans le sens ou cette précondition est garantie par le compilateur, et ne fait pas réellement partie du contrat.

  9. #29
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Quand je code une fonction qui prend un A& en paramètre, je n'ai aucune idée du type réel qui m'est passé. Je sais juste qu'il est A ou dérive de A. Je peux donc vérifier les contrats définis par A, pas plus. Si on me passe un B, et qu'il vérifie les contrats de A, pas de problème. Mon algorithme va fonctionner. Si on me passe un C, qui ne vérifie pas les contrats de A, mais est plus restrictif, ça risque fortement de merder.

    Le LSP me dit que, si je peux substituer un C à un A, alors, tout algorithme qui fonctionnait pour un A, fonctionne pour un C. Ça s'applique complètement aux comportements polymorphes, du moment qu'on respecte le contrat !

    Un héritage qui restreindrait les préconditions, rend faux tout algorithme qui se basait sur ces préconditions. Un héritage qui étend ces préconditions, lui, n'empêche pas ces algorithmes de fonctionner (c'est l'inverse pour les postconditions).
    Mouais, j'avais donc rien compris.
    Bon avec cette explication ça va mieux. L'exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void g(A& param1, int param2)
    {
      if(param2 > 0) // je vérifie que je respecte le contrat pour appeler f
        param1.f(param2);
    }
    avec le petit laïus, c'est convainquant

    [EDIT] : la confusion vient aussi du fait qu'on utilise souvent mal l'héritage : on oublie qu'un héritage publique doit bien représenter un IS-A pour justement garantir le LSP (je dit encore une bêtise?).

  10. #30
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Cela me parait bizarre, car contraire à la covariance et la contravariance.

    Selon ces principes, une classe dérivée est en droit d'étendre les pré-conditions (contravariance pour les paramètres, non-possible par C++) et de restreindre les post-conditions (covariance pour la valeur de retour, autorisée en C++).
    A vrai dire, je ne vois pas où il pourrait y avoir un problème...

    Partons de la définition, courremment acceptée, de l'héritage comme étant une relation "est-un".

    Cette définition est suffisamment "imprécise" pour que l'on puisse la répartir en trois situations distinctes (bien que les deux premières puissent, dans une certaine mesure, s'associer pour partie des comportements avec la troisième ):
    • La classe dérivée est une spécialisation de la classe de base (le chien qui est un animal)
    • La classe dérivée est une généralisation de la classe de base (je n'ai pas d'exemple, mais ca existe)
    • La classe dérivée est une réalisation de la classe de base (elle permet juste la "construction" de la classe de base avec des données particulières: cf la hierarchie d'exception proposée plus haut)

    Dans le premier cas, il semble cohérent d'accepter qu'une condition (qu'elle soit vérifiée avant ou après ne change pas grand chose) soit plus restrictive que la condition de départ.

    Dans le second cas, il semble cohérent d'accepter qu'une condition soit plus laxiste que celle de départ, et dans le dernier cas, la condition doit être strictement identique à celle de départ.

    D'un autre côté, une condition (de manière générale) est destinée à donner le "go" (l'objet est cohérent) ou le "stop" (l'objet n'est pas cohérent), éventuellement assortie d'information permettant de récupérer un état cohérent.

    La seule différence entre pré et post condition est que, la pré condition vérifie que nous sommes bien face à la possibilité d'effectuer une action alors que la post condition nous indique que l'action effectuée a "merdé", et qu'il faut annuler les modifications éventuellement apportées.

    Il ne me semble donc pas aberrant d'accepter, selon le sens d'héritage envisager qu'une condition puisse s'adapter au type réel de l'objet utilisé, et ce, quel que puisse être sa "position" dans la logique

    Maintenant, il se peut que ce raisonnement - peut être un peu trop théorique - ne tienne pas la route, mais, dans ce cas, dites moi où, que je me couche moins bête ce soir que ce que je ne l'étais en me levant ce matin
    Citation Envoyé par white_tentacle Voir le message
    Problème, rien dans le langage ne me le garantit. Et la pratique tend à prouver que ce qui n'est pas garanti, est faux.
    Là, on attaque le problème majeur de la compétence (ou de l'incompétence) du concepteur... et il n'y a effectivement rien qui puisse te prémunir de sa bêtise
    Non, justement, je veux l'empêcher !
    Ca, c'est un problème de communication ...

    La gestion de la qualité passe aussi par le fait que la communication doit pouvoir passer de manière non ambigüe en évitant toute interprétation

    Si tu savais le nombre de fois où j'ai pesté sur le phénomène du "téléphone sans fil", y compris dans des domaines qui n'ont rien à voir avec l'informatique...

    Mais, encore une fois, cela démontre la (non) compétence du gestionnaire
    Mon but, c'est que si je suis sûr qu'appeler f() sur un A va réussir, alors, appeler f() sur un B va réussir aussi. La programmation par contrat me garantit ça, car elle garantit que B::f demande au plus que les préconditions de A::f soient respectées.
    Mais je ne vois pas en quoi l'adaptation du comportement au type réel, pour autant qu'elle n'inverse pas la logique (de renvoyer vrai alors qu'elle aurait du renvoyer faux) serait de nature à empêcher que l'appel de f() ne réussisse...

    Je crois que si tu penses ça, tu passes à côté du but du LSP. Ou alors, c'est moi qui n'ai rien compris au LSP .
    Comme indiqué plus haut LSP ne s'applique qu'aux invariants, aux propriétés intrinsèques de la classe de base, qui sont transmises, sans modification, aux classes dérivées.

    Sinon, dés que tu envisage le polymorphisme, tu en viens à estimer ne plus respecter LSP

    Quand je code une fonction qui prend un A& en paramètre, je n'ai aucune idée du type réel qui m'est passé. Je sais juste qu'il est A ou dérive de A. Je peux donc vérifier les contrats définis par A, pas plus. Si on me passe un B, et qu'il vérifie les contrats de A, pas de problème. Mon algorithme va fonctionner. Si on me passe un C, qui ne vérifie pas les contrats de A, mais est plus restrictif, ça risque fortement de merder.

    Le LSP me dit que, si je peux substituer un C à un A, alors, tout algorithme qui fonctionnait pour un A, fonctionne pour un C. Ça s'applique complètement aux comportements polymorphes, du moment qu'on respecte le contrat !
    Mais qui dit que tu ne peux pas adapter le contrat au type réel

    Le contrat est là pour baliser les conditions dans lesquelles l'algorithme fonctionne, mais si tu adapte (quelle qu'en soit la manière) le contrat au type réel parce que l'algorithme peut fonctionner avec des valeurs différentes si tu ne manipule pas effectivement un objet du type de la classe de base, il n'y a pas de raison que ton algorithme devienne faux
    Un héritage qui restreindrait les préconditions, rend faux tout algorithme qui se basait sur ces préconditions.
    Pas forcément... Pour autant que tu adapte la condition de manière cohérente
    Un héritage qui étend ces préconditions, lui, n'empêche pas ces algorithmes de fonctionner (c'est l'inverse pour les postconditions).
    pas moins que s'il les restreignait
    Une condition non-polymorphe, c'est une restriction inutile. Le tout est de savoir dans quel sens tu as le droit de modifier ta condition :
    - les préconditions peuvent être étendues (ce n'est pas parce que A::f n'accepte pas quelque chose, que B::f n'a pas le droit de l'accepter)
    - les postconditions peuvent être restreintes (si A::f garantit quelque chose, B::f a le droit de garantir quelque chose de plus)
    - les invariants peuvent être restreints (comme les postconditions)
    Non... une condtion peut très bien être que le diviseur ne soit pas nul...

    Cette condition n'a aucun besoin d'être polymorphe, du moins, si l'on excepte la possibilité de manipuler des types primitifs différents, mais trouve sa pleine utilité dans n'importe quelle division si tu veux éviter les erreur de "division par 0"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #31
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Pour moi (et pour Wikipédia aussi), le LSP agit sur tout le contrat.

    Donc, la classe dérivée n'a le droit de modifier que ce qui n'est pas défini par le contrat en question. Contrat qui :
    • garantit à la fonction qu'elle ne sera pas appelée si les pré-conditions ne sont pas respectées
    • garantit que la fonction respectera les post-conditions
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  12. #32
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Mouais, j'avais donc rien compris.
    Bon avec cette explication ça va mieux. L'exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void g(A& param1, int param2)
    {
      if(param2 > 0) // je vérifie que je respecte le contrat pour appeler f
        param1.f(param2);
    }
    avec le petit laïus, c'est convainquant

    [EDIT] : la confusion vient aussi du fait qu'on utilise souvent mal l'héritage : on oublie qu'un héritage publique doit bien représenter un IS-A pour justement garantir le LSP (je dit encore une bêtise?).
    Sauf que, pour moi, ce n'est pas à la fonction de gérer la condition, mais à l'objet manipulé...

    La fonction ne doit gérer - si elle en a les moyens - que le cas où la condition n'était pas remplie (ou laisser remonter l'erreur)

    Maintenant, je n'ai peut être rien compris à la programmation par contrat
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  13. #33
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Pour moi (et pour Wikipédia aussi), le LSP agit sur tout le contrat.

    Donc, la classe dérivée n'a le droit de modifier que ce qui n'est pas défini par le contrat en question. Contrat qui :
    • garantit à la fonction qu'elle ne sera pas appelée si les pré-conditions ne sont pas respectées
    • garantit que la fonction respectera les post-conditions
    Mais cela n'empêche aucunement d'accepter les addenda au contrat, si l'objet réellement manipuler le nécessite...

    Lorsque l'on dit que tout le contrat doit être respecté, il s'agit aussi (et je dirais presque surtout) de respecter les conditions particulières énoncées par ce contrat
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  14. #34
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Mais cela n'empêche aucunement d'accepter les addenda au contrat, si l'objet réellement manipuler le nécessite...
    Pour ça, je dirais plutôt "seulement s'il est marqué dans le contrat qu'on les accepte".

    Si le contrat de la classe de base spécifie que la classe dérivée peut ajouter des pré-conditions, OK. Sinon, la classe dérivée doit fonctionner sans pré-conditions autres que la classe de base.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  15. #35
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par koala01 Voir le message
    D'un autre côté, une condition (de manière générale) est destinée à donner le "go" (l'objet est cohérent) ou le "stop" (l'objet n'est pas cohérent), éventuellement assortie d'information permettant de récupérer un état cohérent.
    Si on reprend :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    class A
    {
    public :
    virtual void foo();[pré-condition p]
    }
    class B :public A
    {
    public :
    virtual void foo();[pré-condition p' plus restrictive]
    }
     
    void Fonction(A& a)
    [Pré-condition : p]
    {
      ASSERT(p(a));
       a.foo();
    }
    Tu ne peux substituer un B à A car tu risque d'avoir p' non rempli.
    Tu donnes le GO alors que l'objet n'est pas cohérent.
    En fait, si j'ai bien compris, en restreignant la pré-condition, tu romps le contrat, donc tu n'a plus B IS-A A.

  16. #36
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Maintenant, je n'ai peut être rien compris à la programmation par contrat
    Je crois que tu as manqué juste trois petites choses :
    1. l'appelant doit être en mesure de vérifier le contrant avant d'appeler la fonction
    2. le comportement du programme final (non buggé) doit être le même qu'on active la vérification de contrat ou pas
    3. dans la programmation par contrat, le LSP s'applique bien sur toutes les fonctions polymorphes

    Après, ça ne veut pas dire que c'est la seule manière de faire, on est bien d'accord . Mais quand on sort de ce cadre, ça n'est plus de la programmation par contrats.

    Mais cela n'empêche aucunement d'accepter les addenda au contrat, si l'objet réellement manipuler le nécessite...
    Pour ça, je dirais plutôt "seulement s'il est marqué dans le contrat qu'on les accepte".
    Même pas... À cause du 1.

  17. #37
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Sauf que, pour moi, ce n'est pas à la fonction de gérer la condition, mais à l'objet manipulé...
    Ca revient au même : a.f() peut être vu comme
    A::F(&a)
    {
    ASSERT(P(a));
    vtable[f](a);
    }
    au quel cas les précondition de A::F doivent être respecté en substituant un B à A. D'où, sinon, violation du LSP

  18. #38
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Je crois que tu as manqué juste trois petites choses :
    1. l'appelant doit être en mesure de vérifier le contrant avant d'appeler la fonction
    2. le comportement du programme final (non buggé) doit être le même qu'on active la vérification de contrat ou pas
    3. dans la programmation par contrat, le LSP s'applique bien sur toutes les fonctions polymorphes

    Après, ça ne veut pas dire que c'est la seule manière de faire, on est bien d'accord . Mais quand on sort de ce cadre, ça n'est plus de la programmation par contrats.



    Même pas... À cause du 1.
    Mazette! Ta condition 2 implique un sacré effort de test si je ne m'abuse

  19. #39
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    2. le comportement du programme final (non buggé) doit être le même qu'on active la vérification de contrat ou pas
    Mazette! Ta condition 2 implique un sacré effort de test si je ne m'abuse
    C'est pourtant tout le but de la programmation par contrat. L'idée est que chaque violation de contrat est une erreur de programmation (je préfère ce terme à celui de bug ).

    Ce qui est génial, c'est qu'en plus, la PPC nous dit qui est en faute :
    - si une précondition est violée, c'est l'appelant qui est en tort
    - si une postcondition ou un invariant est violé, c'est l'appelé

    Plus qu'un effort de test, c'est aussi un énorme effort de documentation et de réflexion du programmeur.

    Cela dit, en pratique, beaucoup laissent la vérification des préconditions activée (pour diagnostiquer correctement les erreurs de programmation restant dans la release).

  20. #40
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Plus qu'un effort de test, c'est aussi un énorme effort de documentation et de réflexion du programmeur.
    C'est sûr que les conceptions post-it, ça marche moyen comme ça

Discussions similaires

  1. redéfinition de méthode
    Par greg08 dans le forum Langage
    Réponses: 8
    Dernier message: 28/10/2008, 14h59
  2. Réponses: 8
    Dernier message: 01/07/2008, 23h15
  3. héritage et redéfinition de méthodes
    Par Braillane dans le forum Langage
    Réponses: 4
    Dernier message: 07/01/2007, 18h47
  4. redéfinition de méthode et invocation
    Par magedo dans le forum Langage
    Réponses: 2
    Dernier message: 05/01/2007, 19h00
  5. [JPanel][LayoutManager] redéfinition de méthodes
    Par _KB_ dans le forum AWT/Swing
    Réponses: 4
    Dernier message: 06/06/2006, 14h22

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo