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 :

Conception : loi de Demeter + ISP


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Enseignant
    Inscrit en
    Septembre 2011
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 43
    Par défaut Conception : loi de Demeter + ISP
    Bonjour à tous,

    Je reviens avec un problème similaire à celui ci : http://www.developpez.net/forums/d16...t-get-classes/ dont j'étais déjà l'auteur.

    Pour résumé et vous éviter de (re)lire l'ancien post, mon projet se compose de :

    * une petite dizaines de classes A, B, C ... qui représentent des objets spécialisés.

    * une classe "core" qui s'occupe de faire évoluer en fonction du temps ma dizaine d'objets issus des classes A, B, C ...

    * une classe "ui"
    qui s'occupe de gérer les interactions clavier/souris/joysticks qui utilise "core"
    par exemple, l'appui sur une touche du clavier exécute la fonction correspondante à l'action codée dans "core" représentée par la touche.

    * une classe "menu"
    qui s'occupe d'un menu textuel qui utilise "core"
    j'ai un menu qui est composé d'une liste de commandes (représentées par des strings). lorsque l'on sélectionne une commande, la classe menu exécute la fonction correspondante à l'action codée dans "core" représentée par la string.

    * une classe "Script_Executor"
    qui s'occupe de gérer un langage de script fait maison qui utilise "core" (décrite schématiquement ci dessous)
    par exemple, la lecture d'une ligne de script exécute la fonction correspondante à l'action codée dans "core" représentée par la ligne de script correspondante.


    ma classe "core" ressemble à ceci

    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
     
    // A, B , C , D... environ 10 classes ayant chacune 3 à 7 paires de get/set et d'appels de fonctions publiques à la classe en question.
     
    class A
    {
    private:
        double m_a1;
        int    m_a2;
        bool   m_a3;
    public:
        A() : m_a1(0), m_a2(0), m_a3(false) {}
        double get_a1() const    {return m_a1;}
        int    get_a2() const    {return m_a2;}
        bool   get_a3() const    {return m_a3;}
        void   set_a1(double a1) {m_a1 = a1;}
        void   set_a2(int    a2) {m_a2 = a2;}
        void   set_a3(bool   a3) {m_a3 = a3;}
        void fctA1(...) {...}
        void fctA2(...) {...}
    };
     
    class B
    {
    private:
        double m_b1;
        double m_b2;
        bool   m_b3;
    public:
        B() : m_b1(0), m_b2(0), m_b3(false) {}
        double get_b1() const    {return m_b1;}
        double get_b2() const    {return m_b2;}
        bool   get_b3() const    {return m_b3;}
        void   set_b1(double b1) {m_b1 = b1;}
        void   set_b2(double b2) {m_b2 = b2;}
        void   set_b3(bool   b3) {m_b3 = b3;}
        void fctB1(...) {...}
        void fctB2(...) {...}
    };
     
    // core a une instance de chacune des classes ci-dessus
     
    class core
    {
    private:
        A m_a;
        B m_b;
        // ...
        //fonctions spécifiques et privées de core
    public:
        double get_a1() const    {return m_a.get_a1();}
        int    get_a2() const    {return m_a.get_a2();}
        bool   get_a3() const    {return m_a.get_a3();}
        double get_b1() const    {return m_b.get_b1();}
        double get_b2() const    {return m_b.get_b2();}
        bool   get_b3() const    {return m_b.get_b3();}
        // ...
        void   set_a1(double a1) {m_a.set_a1(a1);}
        void   set_a2(int    a2) {m_a.set_a2(a2);}
        void   set_a3(bool   a3) {m_a.set_a3(a3);}
        void   set_b1(double b1) {m_b.set_b1(b1);}
        void   set_b2(double b2) {m_b.set_b2(b2);}
        void   set_b3(bool   b3) {m_b.set_b3(b3);}
        // ...
        void fctA1(...) { A.fct1(...);}
        void fctA2(...) {A.fct2(...);}
        void fctB1(...) {B.fct1(...);}
        void fctB2(...) {B.fct2(...);}
        void fctB3(...) {B.fct3(...);}
        void fctB4(...) {B.fct4(...);}
     
        // ...
     
       //fonctions spécifiques et publiques de core
    };
     
    class Script_Executor
    {
    private:
        core& m_core;
        // ...
    public:
        Script_Executor(core& param) : m_core(param) {}
        double get_a1() const    {return m_core.get_a1();}
        int    get_a2() const    {return m_core.get_a2();}
        bool   get_a3() const    {return m_core.get_a3();}
        double get_b1() const    {return m_core.get_b1();}
        double get_b2() const    {return m_core.get_b2();}
        bool   get_b3() const    {return m_core.get_b3();}
        // ...
        void   set_a1(double a1) {m_core.set_a1(a1);}
        void   set_a2(int    a2) {m_core.set_a2(a2);}
        void   set_a3(bool   a3) {m_core.set_a3(a3);}
        void   set_b1(double b1) {m_core.set_b1(b1);}
        void   set_b2(double b2) {m_core.set_b2(b2);}
        void   set_b3(bool   b3) {m_core.set_b3(b3);}
        // ...
    };
    En gros, j'ai trois classes qui utilisent massivement les fonctions de la classe code qui elle s'occupe des 10 objets.

    J'avais déjà demandé de l'aide parce que core était embouteillé par trop de fonctions getClasseAMachin1() setClasseBMachin3() ... au point d'être illisible.

    C'est toujours le cas.

    J'ai écouté vos remarques, acheter le livre "coder efficacement Bonnes pratiques et erreurs à éviter (en C++)" et je me suis retroussé les manches.
    La lecture du livre m'a permis entre autre de prendre connaissance de la loi de Demeter et des principes S.O.L.I.D. (au point de changer ma vie de programmeur. . Vraiment !!)

    Maintenant mon application est un véritable chantier. Mon code se "clarifie" même si mes collègues voient surtout que je me focalise sur l'existant sans rien apporter de nouveau sur le court terme. Ce qui n'est pas la question.

    J’ai parcouru mon projet, et j'ai recherché à appliquer le plus possible la loi S de SRP. Ce fut très enrichissant et intellectuellement stimulant.
    J'ai modifié les RTTI qui servaient de tests de base en grand nombre.
    Je suis allé à la chasse aux set/get discutables en m’efforçant de penser aux comportements et pas qu'aux données.

    Mais je bloque sur le principe ISP.

    Dans mon projet clairement, "core" apparaît comme un "god object" qui contient tout et qui s'occupe de tout. Je suis arrivé à une classe qui gère synchronise ses objets (en fonction du temps) ET qui propose aux autres classes utilisatrices "ui", "menu" et "Script_Executor" des fonctions passerelles afin de respecter la loi de Demeter. Typique:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    void core::flipTimeClasseA() {
       return classeA->flipTime;
    }
    dans mes classes utilisatrices, j'ai par exemple la fonction:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    core->flipTimeClasseA();
    Voila pour la présentation de mon problème. (Désolé de la longueur !!!! )

    Des solutions ?
    J'ai cherché, je ne suis pas resté passif:

    - j'ai cassé la loi de Demeter sur 2-3 objets [pour tester] et réalisé un

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ClasseA* getClasseA() {
    return classeA;
    }
    dans mes classes utilisatrices, j'ai écris :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    core->getClasseA()->flipTime();
    Option Core c'est le top, j'ai 15 fonctions en moins! Si je continue ainsi après 10 getClasseXXX, mon fichier " core" est enfin dépollué de tous ce qui m’embêtait alors. D'un point de vu personnel, j'ai rendu l'écriture du code plus complexe. Pour programmer la suite de l'application, il est nécessaire alors d'avoir en tête toutes les fonctions publiques ... la portée des fonctions étaient avant d'un niveau (exemple: classeA vers core) et passe maintenant à 2 niveaux (exemple: classeA vers Core vers UI) . Je suis en défaut par rapport au DIP
    Je n'en suis pas content.


    - je crée une classe "core_acess"
    C'est à dire une classe vide amie avec "core" qui reprend toutes les fonctions passerelles de core en accédant à la partie privée de "core" et qui sera alors l'intermédiaire entre "core" et les classes utilisatrices "Script_Executor", "menu", "ui".

    Je n'en suis pas content non plus. j'ai l'impression alors de déplacer mon problème ailleurs et de ne pas mieux coller au principes du S.O.L.I.D.


    Qu'en pensez vous ?
    Que feriez vous à ma place ?

    PS: merci d'être arrivé au bout de mon post, j'ai moi même cru ne pas y arriver.

  2. #2
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 770
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 770
    Par défaut
    Je sors le pop-corn parce que cela sent le fil de discussion fleuve

    Sinon Koala01 te dirait Manager" ne veut rien dire à vue de nez, je dirais plus Script_Executor

  3. #3
    Membre confirmé
    Profil pro
    Enseignant
    Inscrit en
    Septembre 2011
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 43
    Par défaut
    Non non, attention !
    Je ne veux pas être l'auteur d'une discussion fleuve hen !!

    J'ai juste un sacré problème d'organisation de mon code (qui n’effraye pas les autres développeurs plus que ça) et qui me pénalise dans mon travail.
    et ce problème je veux le régler

    Alors on peut laisser les flingues dehors, et régler cela de manière pacifique

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 152
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 152
    Billets dans le blog
    4
    Par défaut
    Est-ce que les éléments externes ont connaissance de ce qu'ils doivent appeler dans Core ou juste de Core ?
    Typiquement pour l'UI et les inputs, on a une classe InputsManager dans laquelle on peut donner des callbacks à appeler sur chaque évènement et la fonction qui envoit l'avènement n'a aucune idée de qui va réellement l'intercepter et quelles actions vont en découdre.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Membre confirmé
    Profil pro
    Enseignant
    Inscrit en
    Septembre 2011
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 43
    Par défaut
    Est-ce que les éléments externes ont connaissance de ce qu'ils doivent appeler dans Core ou juste de Core ?

    Non aucune connaissance des éléments externes. Seul Core connait le tout.

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 152
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 152
    Billets dans le blog
    4
    Par défaut
    Si Core est sensé servir de Dispatcher, pourquoi t'amuser à casser l'encapsulation en ajoutant des accesseurs aux classes qu'il encapsule et utiliser ces accesseurs pour appeler directement la classe concernée depuis le code appelant ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

+ Répondre à la discussion
Cette discussion est résolue.

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