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 :

Problème de conception : chargement de config


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut Problème de conception : chargement de config
    Bonjour,

    Je cherche à charger des paramètres de configuration dans mon application.

    1) Je vais donc avoir une classe de départ Config, pour laquelle je n'ai pas encore choisi la conception exacte (singleton/static load). Y a-t-il une préférence parmi ces deux options ?

    2) Mon vrai problème arrive ensuite, lorsque je veux "propager" mes paramètres aux différentes classes.

    a) Ma première idée était de venir simplement chercher depuis ma classe X le paramètre dont j'ai besoin via Config::M_param, avec M_param une variable statique de ma classe Config.
    Inconvénient : mes classes dépendent directement de la Config.

    b) Une autre idée était donc de commencer à initialiser dans chaque classe des variables statiques représentant mes paramètres et de venir ensuite appeler une méthode de chargement load pour éventuellement charger les valeurs d'un fichier de config.
    Problème : il me faut une méthode de base héritée par mes classes pour le chargement. Or, je souhaite garder mes variables en statique car je trouve ça plus logique d'associer les paramètres à une classe plutôt qu'à une instance. Seulement, si je veux garder tout le chargement de la Config en statique, il m'est impossible d'avoir une méthode de chargement héritée par mes classes. Avez-vous une idée ?

    Pour information, j'envisage ensuite d'utiliser le modèle Observer entre la Config et toutes mes classes pour le chargement.

    Merci à vous

  2. #2
    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,

    La première chose à faire, c'est de veiller à parfaitement séparer les différents modules et responsabilités.

    Non seulement au niveau de la gestion des options de configuration, mais aussi -- et surtout -- au niveau des fonctionnalités proposées par ton projet.

    En effet, si ton application travaille en réseau, l'affichage n'aura besoin que des options "cosmétiques" permettant de définir les couleurs, les polices de caractères à utiliser ou la taille de l'affichage. Elle n'aura absolument que faire des options spécifiques au réseau (sauf, évidemment, pour l'éventuel onglet qui doit te permettre de les modifier )

    Chaque "module" de ton application -- base métier, sauvegarde/chargement, réseau, affichage, son, ... -- devrait pouvoir travailler de manière strictement indépendante des autres (à l'exception de quelques "point d'accès" car il faudra bien que ces modules communiquent avec les autres ) et ne devrait donc avoir accès qu'aux options de configuration qui lui sont propres.

    Et les différentes parties d'un module n'ont pas forcément besoin de disposer de toutes les options qui concernent ce module.

    Le module réseau, par exemple, sera composé d'une classe qui aura pour but de créer et de maintenir la connexion active (ConnexionHolder ).

    Cette classe aura besoin des options relatives à cette tâche, à savoir : le nom du serveur à contacter, le port, le protocole à utiliser, le nom d'utilisateur, son mot de passe, et autres auxquelles ne je pense pas forcément.

    Mais on pourrait envisager d'indiquer la méthode de signature, de compression ou simplement d'encodage du contenu émis.

    Ces informations ne sont sans doute absolument pas nécessaire à la classe ConnexionHolder mais seront par contre indispensable à la classe qui s'occupe de la transmission en elle-même. Et cette dernière n'aura absolument pas à s'inquiéter des options relatives à la mise en place et au maintient de la connexion.

    Par contre, il est très vraisemblable (en tout cas, je t'incite à le faire) que tu mettras en place un système fort proche du patron de conception Façade pour permettre aux autres modules de communiquer avec ton module "réseau".

    Cette façade, qui pourrait tout aussi bien être une classe, serait le candidat idéal à qui fournir l'ensemble des options propres au réseau, vu que c'est au travers de cette façade que toutes les fonctionnalités du réseau seront utilisées. Elle serait donc particulièrement bien placée pour transmettre l'option utile à "qui de droit"

    Et bien sûr, tu travaillerais de manière strictement similaire pour tous les modules

    Au final, tu aurais une dernière classe, mettons Application, qui mettrait en présence les différents modules qu'elle utilise, éventuellement en tenant compte d'options qui la concernent directement (si tu as une option --nosound, c'est sans doute pour indiquer à l'application qu'elle ne doit pas activer le système de gestion du son ) et qui "dispatcherait" les options adéquates vers les modules qui en ont besoin.

    Je suis globalement contre le patron de conception singleton parce que j'estime (et je ne suis pas le seul ) qu'il s'agit d'un "anti pattern" dans le sens où le meilleur moyen pour s'assurer qu'une donnée ne sera pas dupliquée, c'est encore de veiller à n'en créer qu'une seule.

    En plus, le patron de conception Singleton utilise forcément une variable statique pour fonctionner.

    Or, une variable statique n'est jamais qu'une variable globale "Facon du chef, enrobée d'accessibilité sur lit de mystère, avec sa sauce OO"

    Au final, je dirais : pas de variables statiques, mais des variables "tout à fait classiques" qui seront, selon le cas, transmises comme argument à une fonction ou au constructeur (sous la forme d'une référence non constante, si besoin) des objets qui en ont réellement besoin
    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

  3. #3
    Membre Expert
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Billets dans le blog
    1
    Par défaut
    Perso, je suis pas trop d'accord, dans tout mes projets (et projets professionnel) les classe de configuration sont des singleton renvoyant les différentes variables.

    Exemple:
    Mon fichier de configuration est un xml classique:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    <configuration>
      <port>4242</port>
      <ip>127.0.0.1</ip>
    </configuration>
    Ensuite ma classe de configuration va aller parser ce fichier et stocker les information sous forme de map:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::map<std::string, std::string> info;
    Pour y accéder j'ai juste besoin d'appelé une des fonction pour récupérer une variable:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int getInt(std::string cle)
    {
       return (toInt(info[cle]));
    }
    std::string getString(std::string cle)
    {
      return (info[cle]);
    }
    (Ce n'est qu'une ébauche de ce que j'ai réellement, mais c'est pour donner une idée).

    Ce système permet de rajouté des variable dans le fichiers de configuration sans avoir à recodé une partie du programme.

    C'est beaucoup plus dynamique que d'avoir un membre d'une classe qui stock un paramètre. Imagine que tu doives rajouter x paramètre pour plusieurs classes, il faudra alors que tu re-développe les constructeurs de tes classes.

    Imagine une classe ayant besoin de 10 paramètre, tu vas pas appelé un constructeur avec 10 variable quand même?
    Imagine que ce constructeur soit appelé 50 fois dans ton code, tu vas aller modifié 50 fois ces lignes?

    C'est pourquoi je pense qu'il est nettement mieux de passer par un singleton pour la configuration, ce n'est pas une partie très sensible d'une application donc on peut se le permettre.

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Bonjour et merci à tous les deux pour vos suggestions .
    C'est intéressant d'avoir ici deux points de vue!

    koala01 :
    Je suis globalement contre le patron de conception singleton parce que j'estime (et je ne suis pas le seul ) qu'il s'agit d'un "anti pattern" dans le sens où le meilleur moyen pour s'assurer qu'une donnée ne sera pas dupliquée, c'est encore de veiller à n'en créer qu'une seule.
    Ok mais charge au(x) développeur(s) de faire attention à ne pas instancier un nouvel objet qui ferait doublon alors, c'est ça? Pourquoi rester trop permissif au niveau de la conception si au final on veut qu'une seule et unique instance ?

    Or, une variable statique n'est jamais qu'une variable globale "Facon du chef, enrobée d'accessibilité sur lit de mystère, avec sa sauce OO"
    En fait, j'avais envisagé de mettre mes paramètres de configuration par défaut en variable statique privée, ce qui réduit quand même la visibilité à la classe considérée, je trouve donc ça pas trop mal a priori.

    Sinon, la Config serait aussi pour toi un objet instanciable classique ?

    Au final, je dirais : pas de variables statiques, mais des variables "tout à fait classiques" qui seront, selon le cas, transmises comme argument à une fonction ou au constructeur (sous la forme d'une référence non constante, si besoin) des objets qui en ont réellement besoin
    Skeud :
    Imagine que tu doives rajouter x paramètre pour plusieurs classes, il faudra alors que tu re-développe les constructeurs de tes classes
    A moins peut être de réaliser quelque chose d'assez générique en passant par exemple en constructeur une structure qui stocke les paramètres de configuration du module donné ? Enfin après, même si la signature des constructeurs restent inchangées, il faudra quand même définir les nouveaux membres pour stocker les nouveaux paramètres donc non c'est pas mieux ^^.

  5. #5
    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
    Citation Envoyé par sfarc Voir le message
    Bonjour et merci à tous les deux pour vos suggestions .
    C'est intéressant d'avoir ici deux points de vue!

    koala01 :

    Ok mais charge au(x) développeur(s) de faire attention à ne pas instancier un nouvel objet qui ferait doublon alors, c'est ça? Pourquoi rester trop permissif au niveau de la conception si au final on veut qu'une seule et unique instance ?
    Ca, ce n'est pas le plus compliqué...

    Tu peux, par exemple, envisager le recours au pimpl idiom:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class SoundConfig;
    class SoundModule{
        public:
           SoundModule(std::string const & configFilename);
           ~SoundModule();
            UnType const & getConfigValue( Identifiant id) const;
        private:
            SoundConfig * config_;
    };
    (UnType et Identifiant sont des types qui te seront utiles pour travailler qu'il s'agit de définir par toi même)
    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
    #include <SoundModule.h>
    class SoundConfig{
        public:
            SoundConfig(std::string const & configFilename){
                /* ... */
            }
            UnType getConfigValue(Identifiant id) const{
                /* ... */
            }
        private:
            std::map<Identifiant, UnType> infos_;
    }
    SoundModule::SoundModule(std::string const & configFilename):
        config_(new SoundConfig(configFilename){
     
    }
    SoundModule::~SoundModule(){
        delete config_;
    }
    UnType SoundModule::getConfigValue(Identifiant id) const{
        return config_->getConfigValue(id);
    }
    Comme tu n'auras, de toutes manières, qu'un seul module son dans ton application, tu auras la certitude qu'il n'y aura qu'une seule instance de SoundConfig et le développeur n'aura jamais la possibilité d'aller créer une nouvelle instance de SoundConfig
    En fait, j'avais envisagé de mettre mes paramètres de configuration par défaut en variable statique privée, ce qui réduit quand même la visibilité à la classe considérée, je trouve donc ça pas trop mal a priori.
    Comme je te l'ai dit, même si c'est une variable privée, cela en fait une variable globale (c'est le gros principe du mot clé static).

    Tu as beau la cacher, tu a beau en restreindre l'accès, ca reste une variable globale, et les variable globales, c'est mal
    Sinon, la Config serait aussi pour toi un objet instanciable classique ?
    A peu de chose près, oui.

    Mais elle serait beaucoup plus proche de la structure FILE que l'on a en C : Tout le monde sait qu'elle existe, mais personne ne sait ce qu'elle contient réellement et tout ce que l'on en sait, c'est la manière dont on peut l'utiliser.

    Dans l'exemple que je viens de donner, la seule chose que l'on en sait (et encore !!) c'est qu'on peut y accéder de manière indirecte au travers de SoundModule et de sa fonction membre getConfigValue
    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

  6. #6
    Membre Expert
    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
    Par défaut
    Citation Envoyé par skeud Voir le message
    Perso, je suis pas trop d'accord, dans tout mes projets (et projets professionnel) les classe de configuration sont des singleton renvoyant les différentes variables.

    Pour y accéder j'ai juste besoin d'appelé une des fonction pour récupérer une variable:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int getInt(std::string cle)
    {
       return (toInt(info[cle]));
    }
    std::string getString(std::string cle)
    {
      return (info[cle]);
    }
    J’ai pas mal utilisé ça, mais je trouve qu’il y a deux défauts principaux à cette approche :
    - la description du fichier de configuration est éparpillée à plein d’endroits (en fait, partout où on utilise des paramètres de l’application), ce qui fait qu’il est difficile de lister tous les paramètres possibles de l’application.
    - l’interface incite à utiliser directement le paramètre de configuration partout où on en a besoin en utilisant Config::get(XXX), où XXX est une chaîne de caractères, ce qui peut amener des bugs idiots dus à des fautes de frappe (et si c’est à un endroit du code où on ne passe pas souvent, ça peut être plus compliqué qu’on ne croit à détecter), ou compliquer le refactoring en cas de changement du nom du paramètre.

    Cela dit, ça fonctionne et c’est simple à utiliser. Je recommande quand même du coup de s’imposer les limites suivantes :
    - un paramètre de configuration en doit être lu qu’une seule fois, dans une fonction d’initialisation de l’objet dédiée
    - la map des paramètres doit être hiérarchisée, de sorte qu’un module ne connaisse que ses propres paramètres et pas ceux des autres modules. Ça protège aussi des conflits de nom (par exemple, un programme qui accèderait à la fois à une bdd et à un service web pourrait avoir deux fois le paramètre username, mais pour deux contextes différents).

    Citation:
    Imagine que tu doives rajouter x paramètre pour plusieurs classes, il faudra alors que tu re-développe les constructeurs de tes classes
    Effectivement, un gros intérêt pour une bibliothèque est de pouvoir rajouter un paramètre tout en gardant la compatibilité binaire.

  7. #7
    Membre Expert
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    - l’interface incite à utiliser directement le paramètre de configuration partout où on en a besoin en utilisant Config::get(XXX), où XXX est une chaîne de caractères, ce qui peut amener des bugs idiots dus à des fautes de frappe (et si c’est à un endroit du code où on ne passe pas souvent, ça peut être plus compliqué qu’on ne croit à détecter), ou compliquer le refactoring en cas de changement du nom du paramètre.
    C'est pourquoi j'utilise en général dans chacun de mes header de classe ou directement dans la configuration un enum ou des constantes définissant le nom des variables du fichier de configuration.

    Une autre implémentation possible est d'avoir un objet configuration par module qui va chercher et charger exclusivement les paramètres qui concerne le module:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <configuration>
      <reseau>
        <port>4242</port>
        <ip>127.0.0.1</ip>
      </reseau>
      <log>
        <fichier>toto.log</fichier>
        <level>DEBUG</level>
      </log>
      ...
    </configuration>
    Voir aussi un fichier de configuration par module.

    Et le constructeur de la classe configuration prend en paramètre le nom du module.
    Cela permet d'avoir dans chaque module une classe de configuration qui contient que les paramètre du module, mais en contrepartis il faut se trimballé l'instance de la classe de configuration un peu partout car on y accède pas en singleton.

    On peut aussi bien configurer la classe singleton qui stock la configuration pour hiérarchisé les information:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    std::map<std::string, std::map<std::string, std::string>> info;
     
    getString(std::string module, std::string cle)
    {
      return(info[module][cle]);
    }
    L'énorme avantage que je vois c'est la modularité de ce fonctionnement, le but étant d'avoir un maximum d'information en dehors du code pour pouvoir changer le comportement de l'application sans recompiler.

    Bien sur, comme toute chose, il faut l'utilisé avec paricimonie pour ne pas être dans la sur-configuration.

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Vos méthodes me semblent plutôt bonnes mais ce qui me gène encore, c'est le fait d'associer les paramètres à un objet lors de son initialisation. C'est pourquoi je préfèrerais garder mes paramètres en variables statiques privées pour chacun de mes modules.

    Voici en brouillon une solution en Qt pour laquelle j'aimerais avoir vos avis sur le fond plus que la forme :

    La configuration QConfig :
    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
    typedef void(*Load)(); // Pointeur sur une méthode de chargement
     
    class QConfig
    {
      public :
        static void load(QString file); // Chargement du Dom depuis un fichier
        static void load(QString module, QMap<QString, QVariant>& params);
        static void attach(Load);
     
      private :
        static void notify();
     
      private :
        static QDomElement* M_pDom; // Stockage du dom
        static QList<Load> M_loads; // Liste des pointeurs sur les méthodes de chargement
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    bool QConfig::load(QString file)
    {
      /* bool ret = StockageDuDomEnVariableStaticPriv();
      if (success) */
        notify(); // Le dom étant chargé, on est prêt à récupérer les paramètres depuis les modules.
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void Config::notify()
    {
      for (int i=0; i<M_loads.size(); ++i)
        (*M_loads[i])();  // Appel aux méthodes statiques de chargement
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    bool QConfig::load(QString module, QMap<QString, QVariant>& params)
    {
      // Récupération des paramètres du module donné dans le dom
    }
    Un module QNetwork :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class QNetwork //: public QModule
    {
      public :
        static void loadParameters();
     
      private :
        static quint16 M_port;
    }
    Prise en compte du nouveau module pour le chargement des paramètres :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QConfig::attach(QNetwork::loadParameters);
    La méthode de chargement des paramètres d'un module :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void QNetwork::loadParameters()
    {
      QMap<QString, QVariant> tabParams;
      tabParams.insert(CFG_PARAM_PORT, M_port);
      Config::load(CFG_MODULE_NETWORK, tabParams);
    }
    Qu'est ce que vous en pensez ?

    Merci

  9. #9
    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
    Citation Envoyé par skeud Voir le message
    Perso, je suis pas trop d'accord, dans tout mes projets (et projets professionnel) les classe de configuration sont des singleton renvoyant les différentes variables.
    Allez, un peu de lecture (ca faisait longtemps que je ne l'avais plus ressorti ce lien )
    Ce système permet de rajouté des variable dans le fichiers de configuration sans avoir à recodé une partie du programme.

    C'est beaucoup plus dynamique que d'avoir un membre d'une classe qui stock un paramètre. Imagine que tu doives rajouter x paramètre pour plusieurs classes, il faudra alors que tu re-développe les constructeurs de tes classes.

    Imagine une classe ayant besoin de 10 paramètre, tu vas pas appelé un constructeur avec 10 variable quand même?
    Imagine que ce constructeur soit appelé 50 fois dans ton code, tu vas aller modifié 50 fois ces lignes?
    Ah, ben, ca me fait penser à cette discussion sur laquelle j'ai été à peu pres le seul à répondre à impéro!

    Et je me suis même fendu d'un petit projet de "correction", qui utilise trois fichiers d'en-tête et que tu trouveras ==>ici<==

    Ne trouves tu pas cela plus propre
    C'est pourquoi je pense qu'il est nettement mieux de passer par un singleton pour la configuration, ce n'est pas une partie très sensible d'une application donc on peut se le permettre.
    C'est beaucoup plus sensible que tu ne pourrais le croire!

    Le problème du singleton, c'est que c'est forcément une variable globale, qui va donc maintenir globalement un ensemble d'informations dont certaines peuvent être sensibles (login et mot de passe, par exemple) et dont tu n'as globalement que faire dans la majorité des cas.

    Tu pourrais en arriver à créer un singleton par module, ce qui aurait au moins déjà l'avantage de faire en sorte que chaque module ne dispose que des informations dont il a réellement besoin.

    Mais c'est oublier un peu vite que chaque module est composé de plusieurs classes qui n'ont sans doute que faire de la plupart des informations auxquelles le singleton donne accès
    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. #10
    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
    Je me permet de revenir sur
    Citation Envoyé par skeud Voir le message
    Imagine une classe ayant besoin de 10 paramètre, tu vas pas appelé un constructeur avec 10 variable quand même?
    Je vais surtout voir à appeler le constructeur avec les paramètres dont il a réellement besoin pour fournir un objet cohérent, et veiller à ce que les autres paramètres soient transmis aux fonctions qui en ont besoin

    Parce que ce que j'ai dit au niveau de l'application et du module s'applique également au niveau de la classe : de nombreuses options utilisées au niveau de la classe ne sont utiles que pour certaines fonctions spécifiques (que ce soit le constructeur ou toute autre, fonction membre ou non, faisant partie de l'interface publique de l'objet en question).

    Si une classe n'a pas besoin d'une information dont une des fonctions spécifique a besoin, il faut juste veiller à transmettre cette information particulière à la fonction qui en a besoin
    Imagine que ce constructeur soit appelé 50 fois dans ton code, tu vas aller modifié 50 fois ces lignes?
    Je ferais surtout passer le développeur responsable de cet état de fait par la fenêtre avant, s'il a la chance d'en réchapper, de le faire écarteler tout en le faisant rotir à petit feu, histoire qu'il comprenne sa douleur

    La création d'objet doit être centralisée et la décision finale de créer ou non l'objet, en lui passant telle ou telle valeur issue de la configuration, doit dépendre d'un système qui permet justement de s'assurer -- de manière transparente pour celui qui demande la création de l'objet -- que l'objet sera créé "comme il se doit" pour respecter la configuration sans que l'utilisateur n'ai le moindre droit de regard sur cette configuration.

    Je m'explique :

    Mettons que l'utilisateur veuille créer un objet spécifique qui sera positionné à un endroit bien donné et que cet objet puisse être d'une couleur spécifiée par la configuration.

    Les seules informations que l'utilisateur doit transmettre au système qui se chargera de créer l'objet, c'est la position de celui-ci ainsi que les éventuelles informations qui permettront, d'une manière ou d'une autre, de déterminer si c'est un objet de type A ou un objet de type B qui sera réellement créé.

    Il n'a strictement que faire de savoir si l'objet en question sera, en fonction de la configuration, bleu ciel, rouge sang ou brun caca doigt

    Tout ce que l'utilisateur doit pouvoir faire, c'est demander au système concerné "crée moi tel objet à tel position" !

    Et c'est au système de déterminer si l’utilisateur a le droit ou non de créer l'objet demandé, quelle couleur lui donner et le meilleur moyen à utiliser pour parvenir au résultat escompté par l’utilisateur
    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. #11
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Allez, un peu de lecture (ca faisait longtemps que je ne l'avais plus ressorti ce lien )
    Sympa comme article . J'étais déjà tombé dessus mais ça fait pas de mal de le relire .

    Tu peux, par exemple, envisager le recours au pimpl idiom:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class SoundConfig;
    class SoundModule{
        public:
           SoundModule(std::string const & configFilename);
           ~SoundModule();
            UnType const & getConfigValue( Identifiant id) const;
        private:
            SoundConfig * config_;
    };
    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
    #include <SoundModule.h>
    class SoundConfig{
        public:
            SoundConfig(std::string const & configFilename){
                /* ... */
            }
            UnType getConfigValue(Identifiant id) const{
                /* ... */
            }
        private:
            std::map<Identifiant, UnType> infos_;
    }
    SoundModule::SoundModule(std::string const & configFilename):
        config_(new SoundConfig(configFilename){
     
    }
    SoundModule::~SoundModule(){
        delete config_;
    }
    UnType SoundModule::getConfigValue(Identifiant id) const{
        return config_->getConfigValue(id);
    }
    Comme tu n'auras, de toutes manières, qu'un seul module son dans ton application, tu auras la certitude qu'il n'y aura qu'une seule instance de SoundConfig et le développeur n'aura jamais la possibilité d'aller créer une nouvelle instance de SoundConfig
    Oui effectivement j'aurais qu'un seul module mais ce qui m'embête, c'est que rien ne l'impose. Rien n'empêche un développeur un jour d'instancier un nouveau module son par exemple même si c'est pas très fute fute.

    Aussi, autre point, l'idée de passer en paramètre du constructeur le chemin du fichier de configuration à chaque module me gène un peu. Cela voudrait dire, que chaque module, ou plutôt chaque objet conf associé a la possibilité de connaître l'ensemble du fichier. Je réfléchie à une alternative pour ce point là .

  12. #12
    Membre Expert
    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
    Par défaut
    Citation Envoyé par sfarc Voir le message
    Aussi, autre point, l'idée de passer en paramètre du constructeur le chemin du fichier de configuration à chaque module me gène un peu. Cela voudrait dire, que chaque module, ou plutôt chaque objet conf associé a la possibilité de connaître l'ensemble du fichier. Je réfléchie à une alternative pour ce point là .
    De mon point de vue, un module doit se configurer… avec une structure qui lui est dédiée.

    Dans l’idéal, tu vas donc avoir :
    - un gestionnaire de settings, générique, qui s’occupe de la persistance (et donc, lecture / écriture du fichier de paramètres).
    - des classes / modules qui prennent quelque part, soit dans le constructeur, soit dans une méthode init, une structure/classe dédiée à leur configuration.
    - pour chaque module, un petit adaptateur qui va se charger de convertir les informations du fichier de configuration vers les classes de configuration, et vice-versa.

    Ainsi, tu as réellement un couplage faible entre la configuration de tes modules et la gestion de ton fichier de configuration. Après tout, le module n’a que faire que le paramètre vienne d’un fichier, de la ligne de commande, soit codé en dur ou encore calculé à partir de l’âge du capitaine : tout ce qu’il a besoin de savoir, c’est sa valeur.

  13. #13
    Expert confirmé
    Avatar de Mat.M
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    8 527
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2006
    Messages : 8 527
    Par défaut
    Citation Envoyé par koala01 Voir le message

    Or, une variable statique n'est jamais qu'une variable globale "Facon du chef, enrobée d'accessibilité sur lit de mystère, avec sa sauce OO"
    c'est tout à fait exact

  14. #14
    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
    En fait, l'idée est, vraiment, de veiller à respecter scrupuleusement le D de SOLID qui est mis pour Dependency Inversion Principle, ou, si tu préfères en français, le principe d'inversion des dépendances.

    Autrement dit, chaque module ne doit exposer, au travers d'une façade, que le stricte nécessaire pour pouvoir être utilisé.

    Et, comme je te l'ai dit, le module "réseau" peut très facilement être considéré comme une "vue particulière", dans le sens où, tout ce qu'elle fait, c'est faire la transition entre des données externes (issues du réseau) à transmettre au modèle et des données internes (issues du modèle) à transmettre au réseau.

    Or, tu dispose d'un système génial pour faire communiquer les modèles avec les différentes vues : le système de signaux et de slots.

    La dépendance entre le module "modèles" et le module "réseau" peut donc être quasiment nulle.

    Je m'explique :

    Tu devrais avoir différents modules dont on pourrait dresser une liste proche de
    • Business : ce module ne contient que des données métier pures et dures
    • Modifications : ce module contient des données qui n'entrent pas tout à fait dans la définition de "données métier", mais qui permettent de manipuler et de modifier celles-ci (par exemple : l'identifiant d'un objet vendu et le prix obtenu lors de la transaction, l'identifiant du monstre attaqué et du sort (ou de l'arme) utilisé(e), ...)
    • Models : ce module contient les différents modèles que tu vas manipuler dans ton application
    • Views : ce module reprend l'ensemble des vue "graphiques" que tu feras afficher par ton application
    • Network : ce module s'occupe de gérer la connectivité avec le réseau.
    • Sound : ce module s'occupe de gérer tout l'aspect sonore de ton application.
    • Y en a peut être d'autres

    Comment va-t-il s'y prendre il va réagir à certains signaux qui sont émis par certains modules (essentiellement le module Network et le module Views).

    Comment pourra-t-il réagir à ces signaux en exposant au travers de sa façade un certain nombre de slots qui pourront être connectés aux différents signaux émis par les différents modules.

    Pour cet aspect spécifique, la façade du module Models se contente donc d'exposer les différents slots susceptibles d'intéresser les différents modèles et de faire la jonction entre ces slots exposés et les slots des modèles en question.

    D'un autre coté, les modifications apportées par le module Models au business sont, elles aussi, susceptibles d'occasionner l'émission de signaux, susceptibles d'intéresser des modules comme Network, Views ou sound.

    La façade de Models va donc exposer ces signaux "intéressants" (comprends : qui sont susceptibles d'intéresser d'autres modules que le module Models) et faire la jonction entre les signaux émis par les différents modèles et ces signaux exposés.

    La façade du module Sound exposera essentiellement des slots, correspondant aux différentes réactions sonores envisagées.

    La façade du module Network et celle du module Views exposera un certain nombre de slots qui seront connectés aux "signaux intéressants" (du point de vue du réseau) émis par le module Models afin de permettre l'émission des modifications apportées "en local" ainsi qu'un certain nombre de signaux -- auxquels viendront se connecter certains slots du module "Models" -- afin de permettre la prise en compte des informations reçues au travers du réseau.

    Enfin, il faut "juste" prendre en compte le fait que les vues "graphiques" sont généralement directement reliées à un modèle particulier.

    L'idée est donc peut être de faire en sorte que la façade de Views puisse permettre d'accéder à une vue particulière et que celle de Models puisse permettre de faire la relation entre une vue et le modèle à utiliser.

    Au final, tous les modules dépendent du module "Modifications" car ce ne sont que des "structures simples" qui représentent les modifications à apporter.

    La façade du module Models peut ressembler à 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
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class ModelsFacade{
        public:
           ModelsFacade(){
               createModels();
               connectSignals();
               connectSlots();
            }
            setModelToView(View * view, ModelId id) const{
                Model * model =/* trouver le modèle correspondant */;
                view->setModel(model);
            }
            /* peut être quelques autres fonctions utilitaires */
         signals :
             /* les différents signaux à exposer */
         public slots:
            /* les différents slots à exposer */
        private:
            void createModels(){
                /* on crée les modèles et on les ajoute à la map */
            }
            void connectSignals(){
                /* on connecte les signaux des différents modèles
                 * aux signaux exposés par la facade
                 */
            }
            void connectSlots(){
                /* on connecte les slots exposés par la façade aux
                 * slots des différents modèles
                 */ 
            }
            /* ce n'est qu'un exemple :D */
            std::map<ModelId, QAbstractModel *> allModels_;
            /* le fameux pimple idiome dont j'ai parlé plus haut */
            ModelsConfig * config_;
    };
    Pour le module Network, on exposerait essentiellement des signaux et des slots avec, peut être, une ou deux fonctions "utilitaires", 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
    class NetworkFacade{
        public:
            NetworkFacade(){
                createConnection();
            }
            /* peut être une ou deux fonctions utilitaires ici */
        signals :
            /* les différents signaux susceptibles d'être émis */
        public slots :
           /* les différents slots de réaction intéressants */ 
        private:
            NetworkConnection connection_;
            /* le fameux pimple idiome dont j'ai parlé plus haut */
            NetwokConfig * config_;
     };
    et la façade du module Views pourrait ressembler à 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
    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
    class ViewsFacade{
        public:
            ViewFacade(){
                createViews();
                connectSignals();
                connectSlots();
            }
            View * getViewById(ViewId id){
                View * found = /* recherche de la vue identifiée par id */
                return found;
            }
        signals :
            /* les différents signaux exposés */
     
         signals :
             /* les différents signaux à exposer */
         public slots:
            /* les différents slots à exposer */
        private:
            void createView(){
                /* on crée les vues et on les ajoute à la map */
            }
            void connectSignals(){
                /* on connecte les signaux des différentes vues
                 * aux signaux exposés par la facade
                 */
            }
            void connectSlots(){
                /* on connecte les slots exposés par la façade aux
                 * slots des différents modèles
                 */ 
            }
            /* ce n'est qu'un exemple :D */
            std::map<ViewId, QAbstractView *> allView_;
            /* le fameux pimple idiome dont j'ai parlé plus haut */
            ViewsConfig * config_;
    };
    Et ton application pourrait parfaitement ressembler à 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
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Application{
        public:
            Application(){
                setupViewModels();
                connectSignals();
             }
        private:
            void setupViewModels(){
                /* on récupère les différentes vues de View et on définit leur modèle
                 * au travers de models_.setModelToView */
            }
            void connectSignals(){
                /* on connecte les signaux émis par les différentes façades aux slots
                 * des façades intéressées 
                 */
            }
            ModelFacade models_;
            ViewFacade views_;
            NetworkFacade network_;
            SoundNetwork sound_;
    };
    Sous une telle forme, tes trois modules peuvent fonctionner de manière strictement autonome.

    Mieux encore, tu peux développer tes différents modules dans l'ordre que tu souhaites, y compris par "petit morceau", sans que cela n'influe en rien sur le fonctionnement du reste

    Bon, bien sur, il faudra toujours tenir compte du module Modifications qui est un module "central" (comprends : dont dépendent tous les autres) et du module Business lorsque tu développera ton module Models, mais ce sont les deux seuls points de liaison entre les différents modules auxquels tu devras être attentif.

    Elle est pas belle la vie
    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

  15. #15
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Par défaut
    Tout paraît plus facile avec tes explications .

    Juste deux points qui sont encore un peu flous :
    1) Le module Business contiendra uniquement la déclaration des données métiers c'est bien ça ?
    2) Le module Modifications n'est pas très clair pour moi. En fait, je ne vois pas bien son intérêt.

    Si je prends l'exemple de récupération de trames réseaux pour le remplissage d'un modèle de données, dois-je faire :

    a) Le module Network récupère les trames, les décode, envoie un signal avec les données décodés, signal qui est connecté à un slot d'un modèle du module Model (ou encore à un slot de la façade du module Model qui se charge de passer les informations aux modèles voulus).

    b) Le module Network récupère les trames, les décode, envoie un signal avec les données décodés, signal connecté à un slot qui met à jour une collection des données récupérées, qui, à sa mise à jour, prévient à son tour le modèle lié à cette collection (le modèle possède un pointeur sur la collection par exemple).

    Dans ces deux cas, je ne vois pas vraiment ou va intervenir le module Modification.

    Merci encore

  16. #16
    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
    Citation Envoyé par sfarc Voir le message
    Tout paraît plus facile avec tes explications .

    Juste deux points qui sont encore un peu flous :
    1) Le module Business contiendra uniquement la déclaration des données métiers c'est bien ça ?
    Et de "tout ce qui va bien pour les manipuler, oui.
    2) Le module Modifications n'est pas très clair pour moi. En fait, je ne vois pas bien son intérêt.
    Hé bien, pose toi la question de savoir ce que tu vas envoyer sur le réseau.

    Vas-tu systématiquement envoyer toutes tes données métiers

    Ou vas-tu plutôt envoyer uniquement les informations qui permettront de reproduire la modification qui a été apportée entre un instant T et l'instant T+1

    Il semble beaucoup plus logique de ne renvoyer que "ce qui a changé" entre T et T+1, et encore, pas forcément la donnée en entier mais juste la "différence" entre l'état de la donnée telle qu'elle était à l'instant T et l'état dans lequel elle se trouve à T+1.

    Cela te permettra de n'envoyer que le "stricte minimum" sur le réseau et d'éviter de le surcharger avec un tas d'informations dont le récepteur n'a que faire.

    C'est d'autant plus justifié que c'est, en gros, la manière de fonctionner des signaux et des slots très spécifiques : quand un signal indique que l'index sélectionné d'un combobox a été modifié, il envoie juste un signal selectedIndexChanged( nouvel index), et le slot qui gère ce signal sait comment réagir en conséquence
    Si je prends l'exemple de récupération de trames réseaux pour le remplissage d'un modèle de données, dois-je faire :

    a) Le module Network récupère les trames, les décode, envoie un signal avec les données décodés, signal qui est connecté à un slot d'un modèle du module Model (ou encore à un slot de la façade du module Model qui se charge de passer les informations aux modèles voulus).

    b) Le module Network récupère les trames, les décode, envoie un signal avec les données décodés, signal connecté à un slot qui met à jour une collection des données récupérées, qui, à sa mise à jour, prévient à son tour le modèle lié à cette collection (le modèle possède un pointeur sur la collection par exemple).

    Dans ces deux cas, je ne vois pas vraiment ou va intervenir le module Modification.
    Encore une fois, pose toi la question de ce que le module Network récupère comme trame.

    Il va récupérer des données, certes, mais il n'y a aucun sens à ce qu'il récupère à chaque fois l'ensemble des données métier, même si cela se limite au données métier modifiées.

    Il va juste obtenir la différence entre l'état des données métier à l'instant T et celui à l'instant T+1. Et il doit juste décoder cette différence.

    Et son rôle s'arrête là : à émettre un signal qui sera "porteur" de la différence à appliquer.

    Parce que c'est le rôle du module "Model" de s'assurer que cette différence est cohérente et d'appliquer cette différence au données métier.

    Et parce que c'est aussi ce qui va se passer au niveau graphique, en réactions aux différents événement comme le clique de la souris ou l'utilisation du clavier

    Maintenant, je parle de "module" de modifications à apporter, c'est eput etre un peu excessif.

    Ce peut très bien n'être que l'ensemble des types primitifs utilisés ... Tout comme ce peut être des structures qui donnent un sens particulier à ces types primitifs en fonction de la différence à appliquer
    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

Discussions similaires

  1. [Conception] - Chargement
    Par defkid dans le forum Général Java
    Réponses: 5
    Dernier message: 24/10/2005, 17h20
  2. Gestion des départements problème de conception
    Par snoopy69 dans le forum Modélisation
    Réponses: 7
    Dernier message: 11/10/2005, 13h08
  3. Réponses: 3
    Dernier message: 08/12/2004, 21h11
  4. Problème de conceptions de tables
    Par dtavan dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 23/05/2004, 23h13
  5. Problème lors du chargement
    Par carlito dans le forum Flash
    Réponses: 26
    Dernier message: 06/01/2004, 15h21

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