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

Langage C++ Discussion :

Le changement de visibilité d'une fonction virtuelle est-il un viol du LSP ?


Sujet :

Langage C++

  1. #101
    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
    En quoi RTTI viole-t-il LSP
    Liskov me dit, si B hérite de A (B est un A), quelque soit P(A) vrai, alors P(B) vrai.

    Soit P(x) = typeid(x) == A;

    P(A) est vrai.
    P(B) est faux.

    CQFD .

  2. #102
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Je traduit ta propriété P par P(x) = "x est un A". (C'est ce que dit le typeid )

    Soit a un A et b un B, on suppose que tout B est un A (hypothèse du principe).
    On a P(a) vraie par définiton, d'autre part on a b est un B, or tout B est un A, donc b est un A (transitivité de "est un"), donc P(b) est vraie. CQFD

    En traduisant ta propriété P hors du contexte C++ je trouve que ca marche mieux.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Liskov me dit, si B hérite de A (B est un A), quelque soit P(A) vrai, alors P(B) vrai.

    Soit P(x) = typeid(x) == A;

    P(A) est vrai.
    P(B) est faux.

    CQFD .
    Oh là, oh là, ho là...

    Typeid n'est absolument pas une propriété de A ou de B, c'est une fonction libre qui récupère l'identifiant (fatalement unique) du type qui lui est passé.

    Ne confondons pas...
    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. #104
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Liskov me dit, si B hérite de A (B est un A), quelque soit P(A) vrai, alors P(B) vrai.

    Soit P(x) = typeid(x) == A;

    P(A) est vrai.
    P(B) est faux.

    CQFD .
    le LSP est un principe de substitution basé sur le comportement d'un objet (behavioral subtyping) et pas sur sa nature.

    typeid(x) renseigne la nature de "x", et pas son comportement.

    On pourrait imaginer avoir deux classes A et B qui n'héritent pas l'une de l'autre mais qui sont substituables (meme méthodes). Ce n'est pas possible en C++ a cause du typage statique, mais ça c'est juste une limitation du langage de programmation et pas du concept objet.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  5. #105
    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
    Je traduit ta propriété P par P(x) = "x est un A". (C'est ce que dit le typeid )
    LSP dit "quelque soit". Changer ma propriété P pour en trouver une similaire mais qui t'arrange, ça n'est pas très honnête . Je dis juste
    vouloir appliquer le LSP de manière trop stricte conduit à des absurdités (le RTTI dont typeid viole le LSP, par exemple).
    Donc oui, je fais quelque chose de stupide, et je n'ai aucune honte à le dire. Mais ceci uniquement dans le but d'ébranler des certitudes.

    Et puis, fondamentalement, typeid me permet vraiment d'écrire explicitement du code qui casse la substituabilité (test sur le type --> exception si pas le bon typeid, je casse toute substituabilité). Contrairement au changement de visibilité en C++ .

    Citation Envoyé par pseudocode Voir le message
    On pourrait imaginer avoir deux classes A et B qui n'héritent pas l'une de l'autre mais qui sont substituables (meme méthodes). Ce n'est pas possible en C++ a cause du typage statique, mais ça c'est juste une limitation du langage de programmation et pas du concept objet.
    Tu as fait la première étape du raisonnement. Te reste à faire la deuxième, et tu seras de mon avis (càd, si changer la visibilité n'empêche pas la substituabilité (c'est le cas en C++, mais ça ne serait probablement pas le cas en python, par exemple), ça ne viole pas le LSP).

  6. #106
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Tu as fait la première étape du raisonnement. Te reste à faire la deuxième, et tu seras de mon avis (càd, si changer la visibilité n'empêche pas la substituabilité (c'est le cas en C++, mais ça ne serait probablement pas le cas en python, par exemple), ça ne viole pas le LSP).
    C'est déjà ce que j'ai marqué au post #89.

    Je me permettais juste de dire que ton exemple avec typeid() n'était pas très représentatif du concept de "comportement".
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    LSP dit "quelque soit". Changer ma propriété P pour en trouver une similaire mais qui t'arrange, ça n'est pas très honnête . Je dis juste

    Donc oui, je fais quelque chose de stupide, et je n'ai aucune honte à le dire. Mais ceci uniquement dans le but d'ébranler des certitudes.

    Et puis, fondamentalement, typeid me permet vraiment d'écrire explicitement du code qui casse la substituabilité (test sur le type --> exception si pas le bon typeid, je casse toute substituabilité). Contrairement au changement de visibilité en C++ .
    Le problème, c'est que tu confond un comportement et son résultat.

    Le comportement de typeid est "récupérer un identifiant unique permettant de savoir à quel type on a affaire".

    Et tu remarquera que ce comportement est valide pour n'importe quel type de donnée, que la substituabilité soit envisagée ou non

    Par contre, c'est le résultat du comportement qui te permet de déterminer ta logique, et non le comportement en lui-même.

    Or, le principe même du polymorphisme est de permettre d'adapter un comportement en fonction du type auquel on a réellement affaire.

    Le fait que le résultat d'un comportement te permette ou non de considérer deux types comme étant substituables n'intervient donc absolument pas dans la décision.

    S'il en était autrement, LSP ne serait jamais qu'un principe inapplicable, car tu ne pourrais en aucun cas estimer que la redéfinition d'une fonction dans un type dérivé permet malgré tout de le respecter, et donc, le fondement même de l'héritage serait une pure ineptie, et le modèle (ou les modèles) objet(s) s'effondrerai(en)t "comme un château de carte"
    Tu as fait la première étape du raisonnement. Te reste à faire la deuxième, et tu seras de mon avis (càd, si changer la visibilité n'empêche pas la substituabilité (c'est le cas en C++, mais ça ne serait probablement pas le cas en python, par exemple), ça ne viole pas le LSP).
    Je le répète: Il n'y a aucune raison de prendre le langage utilisé en compte au moment de déterminer si LSP est respecté (par contre, il peut être intéressant de le prendre en compte tout de suite après ) : il n'est jamais fait référence au moindre langage de programmation dans les différents énoncés du principe !!!

    Comme je l'ai dit, LSP doit être un "Go / No go" qui doit être pris en compte avant même de s'intéresser à ce que peut autoriser ou interdire le langage.

    Tu te plains de ne pas pouvoir travailler "hors contexte", mais tu dois veiller à te poser les bonnes questions au bon moment:

    Le contexte dans lequel tu dois t'intéresser au respect de LSP est le contexte dans lequel ton analyse des besoins est terminée et où tu commence à te poser la question des types dont tu va avoir besoin pour rencontrer ces besoins.

    A l'extrême limite, tu pourrais presque te dire
    Bon, je n'ai pas encore déterminé le langage que je vais utiliser (différents décideurs sur le sujet ne se sont pas encore mis d'accord ), mais je sais que, pour répondre aux besoins qui sont mis en évidence, il me faudra le type A et le type B qui sont finalement particulièrement proche.

    Est il sensé d'envisager de faire hériter B de A

    Ben, si LSP est respecté, je peux l'envisager, autrement, je ne peux pas
    et revenir une semaine plus tard sur tes notes (parce que les décideurs se sont enfin mis d'accord sur le langage utiliser ), et contiuer ta conception en des termes proches de
    Bon, j'avais déterminé qu'il me fallait le type A et le typ B...

    Ces deux types sont forts proches, il serait peut être intéressant d'envisager l'héritage, parce que je suis dans une situation dans laquelle je peux encore l'envisager (ex: il n'y a pas encore d'autre héritage si le langage n'autorise pas l'héritage multiple)

    Qu'avais-je dis au sujet de cette possibilité
    Selon tes notes, si LSP est respecté, la réponse sera
    OK... j'avais déterminé que LSP était respecté, je fais donc hériter B de A
    autrement, elle sera du genre de
    Ouppsss... je m'étais dis que LSP n'était pas respecté, je dois donc trouver une autre solution (sans doute basée sur une agrégation ou sur la possibilité de convertir l'un en l'autre)
    Je suis d'accord que cette approche est presque caricaturale des problèmes auxquels on peut être confronté avec les décideurs, et qu'elle n'est pas flatteuse pour ces derniers, mais tu t'es peut être déjà retrouvé dans une situation similaire
    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

  8. #108
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Je le répète: Il n'y a aucune raison de prendre le langage utilisé en compte au moment de déterminer si LSP est respecté (par contre, il peut être intéressant de le prendre en compte tout de suite après ) : il n'est jamais fait référence au moindre langage de programmation dans les différents énoncés du principe !!!
    L'énoncé du concept n'est valide que dans le contexte de la programmation orienté objet. Si ton langage ne respecte pas le paradigme OO (ou permet de l'ignorer dans le cas étudié), le LSP n'est pas applicable.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par pseudocode Voir le message
    L'énoncé du concept n'est valide que dans le contexte de la programmation orienté objet. Si ton langage ne respecte pas le paradigme OO (ou permet de l'ignorer dans le cas étudié), le LSP n'est pas applicable.
    Ah, il est clair que le contexte de base est une conception OO, mais cela me semblait aller de soi: vouloir appliquer un principe de conception OO alors que l'on n'utilise pas le paradigme en question semble quelque peux compliqué

    Mais comme, en règle général, il est possibles de trouver plusieurs langage appliquant un paradigme donné (les langages multi paradigmes étant finalement l'exception ), le choix du langage sera oritenté enté en fonction du (éventuellement des) paradigme(s) que l'on veut utiliser, et non l'inverse
    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

  10. #110
    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
    S'il en était autrement, LSP ne serait jamais qu'un principe inapplicable, car tu ne pourrais en aucun cas estimer que la redéfinition d'une fonction dans un type dérivé permet malgré tout de le respecter, et donc, le fondement même de l'héritage serait une pure ineptie, et le modèle (ou les modèles) objet(s) s'effondrerai(en)t "comme un château de carte"
    L'intérêt du LSP, c'est que si B est-un A au sens LSP, tout le code que j'ai écrit pour A reste valide pour B. Je ne parle pas du code de A, ni de celui de B, mais bien du code de tous les clients de A (codes qui utilisent un A).

    Avec typeid et C++, je peux écrire du code pour lequel ce n'est pas le cas. (ce faisant, je casse la substituabilité de mes objets). Avec le changement de visiblité et C++, je ne peux pas (je ne casse pas la substituabilité de mes objets).

    Mais tu dis que le second mécanisme viole le LSP, et pas le premier ? Ça ne te semble pas plutôt l'inverse ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    L'intérêt du LSP, c'est que si B est-un A au sens LSP, tout le code que j'ai écrit pour A reste valide pour B. Je ne parle pas du code de A, ni de celui de B, mais bien du code de tous les clients de A (codes qui utilisent un A).
    Ben, qu'ai-je écrit "S'il en était autrement (comprend: si la redéfinition d'un comportement dans la classe dérivée venait à rendre ce comportement invalide pour la classe dérivée), LSP serait un principe inaplicable, parce que tu ne pourrais jamais redéfinir un comportement de la classe de base dans la classe dérivée.

    Autrement dit, le fait qu'un comportement soit polymorphe serait en désaccord avec LSP, et donc, tu ne pourrais envisager LSP que pour des héritages dans lesquels le résultat d'un comportement de la classe dérivée serait strictement identique au résultat obtenu par la classe de base.

    Tu avouera que cela n'a strictement aucun sens
    Avec typeid et C++, je peux écrire du code pour lequel ce n'est pas le cas. (ce faisant, je casse la substituabilité de mes objets). Avec le changement de visiblité et C++, je ne peux pas (je ne casse pas la substituabilité de mes objets).
    Si ce n'est que c'est le résultat de typeid qui est différent.

    Cela revient exactement au même que si tu décidais que la fonction virtuelle foo d'un type dérivé devait renvoyer une exception parce que l'on ne peut pas utiliser cette fonction foo dans le cas du type dérivé:

    Un code proche 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
    class Base
    {
        public:
            virtual void addChildren() = 0;
    }
    class Leaf : public Base
    {
        public:
            virtual void addChildren(Base *)
            {
                throw CanNotHaveChildren();
            }
    };
    class Node : public Base
    {
        public:
            virtual void addChidren(Base * b)
            {
                children_.push_back(b);
            }
        private:
            std::vector<Base*> children_;
    };
    ne rend absolument pas le comportement addChildren invalide, bien que le résultat du comportement soit différent selon que l'on exécute addChildren au départ d'une instance de Leaf ou d'une instance de Node (toutes deux passant pour être... une instance de Base).

    Pour typeid, le principe reste exactement similaire: le comportement reste tout à fait valide, mais le résultat du comportement est différent.

    Et comme tu utilise le résultat du comportement pour décider de la logique à suivre, il est tout à fait normal que tu rentre dans une branche de différente de la logique

    Mais comme le comportement (et non le résultat du comportement) reste parfaitement valide, tu n'est absolument pas en désaccord avec LSP.

    D'ailleurs, on remarque que le comportement reste tout à fait valide y compris pour les types non substituables (n'intervenant absolument pas dans la même hiérarchie de classe).

    Mais tu dis que le second mécanisme viole le LSP, et pas le premier ? Ça ne te semble pas plutôt l'inverse ?
    Quels sont les comportements dont tu parles
    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

  12. #112
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Cela revient exactement au même que si tu décidais que la fonction virtuelle foo d'un type dérivé devait renvoyer une exception parce que l'on ne peut pas utiliser cette fonction foo dans le cas du type dérivé:

    ne rend absolument pas le comportement addChildren invalide, bien que le résultat du comportement soit différent selon que l'on exécute addChildren au départ d'une instance de Leaf ou d'une instance de Node (toutes deux passant pour être... une instance de Base).
    !!?

    heu... si ! Le comportement est invalide, du moins en terme de conception orienté objet. Une classe dérivée décrit une spécialisation du comportement, elle ne peut pas faire "moins de choses" que la classe mère !

    Dans ton exemple, tu utilises le "mécanisme" d'héritage pour faire autre chose que de la spécialisation de comportement. On n'est donc plus dans les concepts orienté objet, et donc le principe du LSP ne s'applique pas.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par pseudocode Voir le message
    !!?

    heu... si ! Le comportement est invalide, du moins en terme de conception orienté objet. Une classe dérivée décrit une spécialisation du comportement, elle ne peut pas faire "moins de choses" que la classe mère !
    A condition que le comportement de la classe mère soit déjà défini:

    Dans l'exemple que je donne, la le comportement addChildren ne fait absolument rien, alors, comment veux tu que n'importe quelle redéfinition en fasse moins

    Dans ton exemple, tu utilises le "mécanisme" d'héritage pour faire autre chose que de la spécialisation de comportement. On n'est donc plus dans les concepts orienté objet, et donc le principe du LSP ne s'applique pas.
    Justement, non, je spécialise un comportement donné (le fait d'essayer d'ajouter un enfant) en fonction de ce qu'il est cohérent d'accepter

    Et cela te démontre bien que le résultat peut être totalement différents (simplement impossible à définir pour la classe de base parce qu'elle ne dispose pas des informations permettant de le définir, lancement d'une exception dans une classe dérivée parce que cela n'a puremet et simplement pas de sens d'accepter d'ajouter un enfant à quelque chose qui est connu comme étant une feuille, et carrément accepté pour le type dérivé qui... autorise le fait de définir un enfant).

    Nous sommes tout à fait dans le cadre de LSP parce que l'appel d'un comportement est valide aussi bien pour la classe de base que pour les classes dérivées, bien que le résultat de ce comportement soit clairement différent en fonction du type réellement manipulé.
    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. #114
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    LSP dit "quelque soit". Changer ma propriété P pour en trouver une similaire mais qui t'arrange, ça n'est pas très honnête . Je dis juste
    Je ne change pas ta propriété, je l'abstrait juste du langage.
    When typeid is applied to a glvalue expression whose type is a polymorphic class type (10.3), the result refers
    to a std::type_info object representing the type of the most derived object (1.8) (that is, the dynamic
    type) to which the glvalue refers.
    (Norme, on est bien dans le cas de type polymorphique), donc la propriété P est bel et bien "le type dynamique de x est A". Or le LSP ne distingue pas type dynamique et statique, le seul type considéré est le type réel, ie dynamique (cf plusieurs messages de Koala plus tôt dans la discution), donc la propriété P est "x est de type A", et appliqué a un objet de type B avec "tout B est un A", on obtient que P est valide pour tout B. Je ne vois pas de malhonnêteté dans mon raisonnement. (Je démontre juste que la propriété P que tu exhibes n'invalide pas le LSP quelque soit les types A et B, avec B fille de A).

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Je ne change pas ta propriété, je l'abstrait juste du langage.

    (Norme, on est bien dans le cas de type polymorphique), donc la propriété P est bel et bien "le type dynamique de x est A". Or le LSP ne distingue pas type dynamique et statique, le seul type considéré est le type réel, ie dynamique (cf plusieurs messages de Koala plus tôt dans la discution), donc la propriété P est "x est de type A", et appliqué a un objet de type B avec "tout B est un A", on obtient que P est valide pour tout B. Je ne vois pas de malhonnêteté dans mon raisonnement. (Je démontre juste que la propriété P que tu exhibes n'invalide pas le LSP quelque soit les types A et B, avec B fille de A).
    Le fait est que tu t'intéresse au résultat d'un comportement, qui, manque de bol, renvoit un résultat unique permettant de déterminer de manière unique le type le plus dérivé.

    Le résultat du comportement est donc, effectivement, différent quel que soit le type réel envisagé, mais le comportement reste, quant à à lui, clairement identique.

    Il n'y a donc strictement aucune différence entre ce que je te présente avec une fonction virtuelle: le comportement reste tout à fait similaire, même si le résultat est fondamentalement différent .

    LSP est donc parfaitement respecté, ne serait-ce que parce que cette fonction sort à la limite totalement du contexte même de la substituabilité:

    Même si cela n'a pas énormément de sens, tu pourrais parfaitement envisager d'invoquer typeid sur une classe qui n'entre dans aucune hiérarchie de classe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Class A
    {
    };
    void foo()
    {
        A *a=new A; 
        if(typeid(*a))
        {
            /* n'importe quoi */
        }
    }
    n'a, il faut bien l'avouer, pas énormément de sens mais est tout à fait valide
    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

  16. #116
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    J'ai du mal a voir ce que tu veus dire koala, l'énoncé dit "toutes propriétés valide sur un A l'est sur un B", un propriété sur un bojet est une application qui a un objet associe une valeur booléen. Donc P(x) = "typeid(x) == typeid(A)", est bel est bien une propriété.

    Là où je ne suis pas d'accord avec white_tentacle, c'est qu'il utilise le contexte du C++ pour vérifier cette propriété, fonctionnement du C++ qui "invalide" la propriété . Mais si on abstrait la propriété (P(x) = "x est un A"), alors il n'y a aucun problème.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    struct A
    {
    	virtual void foo()
    	{ assert(typeid(*this) == typeid(A)); std::cout << 0; }
    	virtual ~A(){}
    };
     
    struct B : A {};
    Il me semble que c'est l'idée qu'exprimait white_tentacle. L'appel à foo n'est plus valide (assertion) si l'objet est en réalité un B, donc à fortiori le comportement est clairement différent.

    NB: Ce code n'a aucun utilité pratique AMA, il ne sert qu'à illustrer, un peu comme ton code dans le message précédent

  17. #117
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par koala01 Voir le message
    A condition que le comportement de la classe mère soit déjà défini. Dans l'exemple que je donne, la le comportement addChildren ne fait absolument rien, alors, comment veux tu que n'importe quelle redéfinition en fasse moins
    Justement, non, je spécialise un comportement donné (le fait d'essayer d'ajouter un enfant) en fonction de ce qu'il est cohérent d'accepter
    Tu remarqueras que tes deux arguments sont un peu contradictoires.

    Soit la classe de base définit bel et bien un comportement "ajouter un enfant", soit elle ne définit pas un comportement et alors cette méthode ne devrait pas être publique, et donc on n'est plus dans un concept objet.

    L'idée de définir une signature de méthode "a priori" sans définir son comportement, ce n'est pas trop de l'objet. D'autant plus si classe dérivée n'implémente pas ce comportement et qu'on se retrouve obligé de lever une exception quand on appelle la méthode :/
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    J'ai du mal a voir ce que tu veus dire koala, l'énoncé dit "toutes propriétés valide sur un A l'est sur un B", un propriété sur un bojet est une application qui a un objet associe une valeur booléen. Donc P(x) = "typeid(x) == typeid(A)", est bel est bien une propriété.
    Si ce n'est que typeid n'est absolument pas une propriété des différents type: c'est une fonction libre qui récupère "quelque chose "(que l'on pourrait effectivement considérer comme étant une propriété) qui permet d'identifier le type réellement utilisé de manière unique.

    Une propriété pour un type donné ne peut absolument pas être indépendante du type en question, qu'elle se retrouve ou non dans les types parent, et qui doit se retrouver dans les types dérivés.

    Or, typeid est totalement indépendant de l'objet qu'on lui passe en paramètre.

    L
    à où je ne suis pas d'accord avec white_tentacle, c'est qu'il utilise le contexte du C++ pour vérifier cette propriété, fonctionnement du C++ qui "invalide" la propriété . Mais si on abstrait la propriété (P(x) = "x est un A"), alors il n'y a aucun problème.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    struct A
    {
    	virtual void foo()
    	{ assert(typeid(*this) == typeid(A)); std::cout << 0; }
    	virtual ~A(){}
    };
     
    struct B : A {};
    Il me semble que c'est l'idée qu'exprimait white_tentacle. L'appel à foo n'est plus valide (assertion) si l'objet est en réalité un B, donc à fortiori le comportement est clairement différent.
    L'appel reste valide: tu n'a absolument aucune erreur à la compilation ni même à l'exécution lors de l'appel de typeid.

    Ce qui se passe, c'est que if(typeid(*this)==typeid(A) ne compare absolument pas le comportement (qui reste identique qu'il s'agisse de l'invoquer en passant *this ou en passant A), mais compare le résultat de ce comportement pour décider de la logique à suivre.

    Si donc, même, on considère le fait de pouvoir appeler typeid sur une instance d'objet ou sur un type comme étant une propriété, il faut se rendre contre que cette propriété est totalement valide, quel que soit le type passé en paramètre.

    Or comme il est carrément possible (bien que nous soyons d'accord pour dire que cela n'a pas vraiment de sens ) d'invoquer cette propriété sur un objet qui n'intervient même pas *forcément* dans le contexte de l'héritage, on se rend compte que cette propriété ne peut absolument pas être mise en relation avec le concept de substituabilité, et n'a donc absolument rien à voir avec le respect (ou non) de LSP.
    NB: Ce code n'a aucun utilité pratique AMA, il ne sert qu'à illustrer, un peu comme ton code dans le message précédent
    Nous sommes bien d'accord là dessus
    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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par pseudocode Voir le message
    Tu remarqueras que tes deux arguments sont un peu contradictoires.
    Absolument pas...

    Tu remarque que je ne définis absoluement aucun comportement pour la classe Base...

    C'est d'ailleurs pour cela que, si le compilateur accepte que je manipule une référence ou un pointeur sur Base, il n'acceptera absolument pas que je crée une instance de Base sans autre précision
    Soit la classe de base définit bel et bien un comportement "ajouter un enfant", soit elle ne définit pas un comportement et alors cette méthode ne devrait pas être publique, et donc on n'est plus dans un concept objet.
    Pourquoi

    Le fait d'être en mesure d'invoquer un comportement qui essaye d'ajouter un enfant est une propriété qui est littéralement valide pour n'importe quel objet passant pour être de type Base.

    Le fait que les comportements soient adaptés en fonction du type réel n'invalide absolument pas la propriété associée
    L'idée de définir une signature de méthode "a priori" sans définir son comportement, ce n'est pas trop de l'objet. D'autant plus si classe dérivée n'implémente pas ce comportement et qu'on se retrouve obligé de lever une exception quand on appelle la méthode :/
    Pourquoi

    Dois-je te rappeler que l'exemple que je donne est l'exemple type d'un design pattern connu

    Le fait de lancer une exception est "un moyen comme un autre" d'assurer la remontée d'erreur, même si, nous sommes bien d'accord, l'utilisation qui sera faite de cette hiérarchie devrait faire en sorte d'éviter de se placer dans une situation dans laquelle l'exception sera lancée.

    Mais, si on considère que la propriété "est susceptible de permettre l'ajout d'un enfant" est valide pour "n'importe quel objet passant pour être du type Base", le fait qu'il n'y ait pas de comportement clairement défini pour Base et que le comportement occasionne un résultat différent pour Leaf que pour Node ne viole absolument pas LSP: le comportement associé à cette propriété est valide, même si le résultat est différent parce qu'adapté au type réellement manipulé.

    Encore une fois, il ne faut pas confondre le comportement associé à une propriété et le résultat de ce comportement
    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

  20. #120
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Encore une fois, il ne faut pas confondre le comportement associé à une propriété et le résultat de ce comportement
    Je ne confond pas les deux (cf post #104) et je suis d'accord avec ton jugement sur l'exemple avec typeid.

    Tu remarque que je ne définis absoluement aucun comportement pour la classe Base...
    C'est bien le problème. Une classe, meme avec des methodes abstraites/virtuelles doit définir un comportement. Sinon ce n'est plus une conception d'objet, c'est juste une liste de signature de fonctions.

    Le fait d'être en mesure d'invoquer un comportement qui essaye d'ajouter un enfant est une propriété qui est littéralement valide pour n'importe quel objet passant pour être de type Base.
    Non. On n'invoque pas un comportement. On invoque une méthode. Et le fait d'être en mesure d'invoquer une méthode ca ne valide rien du tout, a part que le code compile correctement.

    Le fait que l'implémentation de la méthode respecte le "contrat" du comportement de la classe, ça oui, ca valide le comportement.

    Et ce que dit le LSP, c'est qu'un comportement existant ne doit pas être supprimé lors d'un héritage. On peut éventuellement ajouter ou enrichir les comportements, mais pas les supprimer.

    Le fait qu'en C++ (java, c#) on hérite automatique des méthodes publiques, ca implique alors que les méthodes publiques ne doivent être utilisées uniquement que pour décrire des comportements. C'est d'ailleurs une bonne pratique en Java/C# de passer par la définition d'interface pour spécifier des comportements et de les faire hériter à la classe.

    Mais comme je l'ai dit, tout cela n'a de sens que si on reste dans un model de conception orienté objet. Si on utilise le "mécanisme" d'héritage pour ce qui est somme toute du templating ou de la réutilisation de code, tout cela ne s'applique pas. Et ton exemple de base/node/leaf entre a mon sens dans cette catégorie.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

Discussions similaires

  1. Réponses: 2
    Dernier message: 02/10/2008, 16h37
  2. Fonction appelant une fonction virtuelle pure
    Par oodini dans le forum C++
    Réponses: 12
    Dernier message: 19/09/2008, 08h24
  3. Une fonction virtuelle ne peut pas retourner un template!
    Par coyotte507 dans le forum Langage
    Réponses: 10
    Dernier message: 08/02/2008, 20h39
  4. Problème de visibilité d'une fonction
    Par hello2lu dans le forum VBA Access
    Réponses: 8
    Dernier message: 03/07/2007, 15h20
  5. Réponses: 2
    Dernier message: 05/03/2006, 19h29

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