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++

  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 772
    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 772
    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 153
    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 153
    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 153
    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 153
    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.

  7. #7
    Expert confirm�
    Homme Profil pro
    D�veloppeur informatique
    Inscrit en
    F�vrier 2005
    Messages
    5 505
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 53
    Localisation : France, Val de Marne (�le de France)

    Informations professionnelles :
    Activit� : D�veloppeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : F�vrier 2005
    Messages : 5 505
    Par d�faut
    Avec le vrai nom des classes et des m�thodes, �a serait moins abstrait.

    Je ne comprends pas pourquoi vous avez l'impression que la classe "Core" devient un "God Object".

    Votre description fait de lui la classe d'interface entre votre code m�tier et le code d'IHM/Scripting.

    Les classes d'IHM et de Scripting n'ont pas � connaitre les classes A-B-... qui impl�mentent le m�tier. Seul l'API pr�sent�e par "Core" n'a � �tre connue par les classes d'IHM et de Scripting.

  8. #8
    Expert �minent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    �ge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activit� : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par d�faut
    Salut,

    Si j'ai bien compris, ta classe core devrait sans doute agir comme une grosse facade pour le reste de ton application, dans le sens o� elle expose un ensemble de fonctionnalit�s "standardis�es" pour que les autres �l�ments (ui, script_executor et menu) puissent y profiter "facilement" d'une interface destin�e � "cacher" la complexit� de tes donn�es m�tier.

    Il se peut que je me trompe, mais tu pourrais peut-�tre envisager de "scinder" cette fa�ade en au moins deux parties:
    • une partie qui n'exposerait que les fonctionnalit�s propres au menu et
    • une partie qui n'exposerait que les fonctionnalit�s propres � l'ex�cution des scripts

    (�tant entendu que ce qui a trait � l'ui a de bonnes chances... d'avoir besoin des deux facades pour fonctionner correctement)

    Maintenant, pour le respect de l'ISP, c'est relativement simple :

    Java (par exemple) vient avec la notion d'interface, qui cache le fait que ce sont des classes ne pr�sentant que des fonctions virtuelles pures, et le fait que le mot cl� implements occasionne un h�ritage de la classe qui impl�mente l'interface vis � vis de l'interface.

    Mais C++ ne fait pas cette distinction "artificielle" entre une classe (qui peut servir de "classe de base" dans un h�ritage) et une interface. Mieux encore : il n'oblige absolument pas � cr�er des fonctions virtuelles pures dans tes interfaces.

    Tu pourrais donc cr�er des classes "holder" (propri�taires ou non) pour les diff�rentes fonctionnalit�s que ta (tes) facades doivent exposer, quelque chose comme
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Class1Hoder{
    public:
        /* en C++, tu peux très bien envisager d'implémenter toutes 
         * ces fonctions, qui n'ont pas forcément besoin d'être virtuelles
         * et qui peuvent donc ne pas être virtuelles pures
         */
        void modify(/* params*/);
       Type getSome() const;
       void someClass1Functionnality(/* params */);
    private:
        Class1 data_;
    };
    qui, tu t'en rend compte � la lecture du code, ne ferait en r�alit� qu'une chose : exposer les diff�rentes fonctionnalit�s dont tu as besoin pour manipuler un �l�ment de type Class1 (et, accessoirement, maintenir les donn�es de type Class1 en m�moire "aussi longtemps que n�cessaire".

    Bien sur, tu ferais cela pour les donn�es auxquelles ta (tes) facade(s) sont destin�es � donner acc�s.

    Par la suite, la cr�ation d'une (de) facade(s) pourrait tr�s bien se limiter �... un h�ritage public (et multiple) de l'ensemble des interfaces dont elles ont besoin, sous une forme 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
    class MenuFacade : public class1Holder, // Class1, Class2 et Class3 sont utilisées pour les menus
                                  public class2Holder,
                                  public class3Holder{
    public:
        /* on ne veut surtout pas autoriser la copie ou l'affectation de
         * notre classe
         */
        MenuFacade(MenuFacade const &) = delete;
        MenuFacade& operator = (MenuFacade const &) = delete;
        /* oui, ca fait très "singleton" hein ?
         * Fais juste attention à n'y avoir recours que... pour ce
         * qui a trait aux menus ;)
         */
        static MenuFacade& get(){
            static MenuFacade instance;
            return instance;
        }
    private:
        MenuFacade() = default;
        ~MenuFacade() = default;
    };
    Et, bien sur, tu pourrais faire pareil avec la facade exposant les fonctionnalit�s de scripts (et les interfaces correspondant aux diverses fonctionnalit�s qu'elle doit exposer)

    Notes bien que je parle de plusieurs facades distinctes, mais ce pourrait tout aussi bien �tre ta classe core, qui h�riterait alors de l'ensemble des interfaces cr��es

    Car ta classe core (ou les diff�rentes facades dont je parle) ne sont en d�finitive que... des classes "utilitaires" qui se contentent de regrouper (et d'exposer) un ensemble de fonctionnalit�s "qui vont bien ensemble" (car toutes dirig�es vers un m�me objectif).

    L'avantage de travailler de la sorte est que, si tu as un probl�me avec une des fonctionnalit�s, tu pourras t'int�resser uniquement aux fonctions expos�es par le holder ad�quat, sans risque un "parasitage" des 250 autres fonctionnalit�s expos�es par les autres holders.

    Comme tu le vois, on en revient toujours au SRP : une classe ne s'occupe que d'une seule et unique chose, mais veille � s'en occuper correctement
    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

+ 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