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 : strategie, barriere, facade et autres patterns


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2009
    Messages
    118
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 118
    Points : 27
    Points
    27
    Par défaut Conception : strategie, barriere, facade et autres patterns
    Bonjour,

    Je travaille actuellement sur un projet dans lequel je souhaite faire le point sur de nombreux points de conception.
    Dans ce projet, j'ai un grand nombre de classes qui ont en commun d'avoir un identifiant numérique unique. L'identifiant n'est pas toujours du même type.
    Au départ, cet identifiant représente la clef de la table SQL que la classe représente. Je reviendrais plus tard sur l'aspect base de données.

    Pour représenter ce caractère commun mais variable en type, j'ai crée une classe template :

    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
     
    #ifndef __TableTrait_h__
    #define __TableTrait_h__
     
    #include <QtCore/QString>
    #include <QtCore/QStringList>
    #include <QtCore/QList>
    #include <QtCore/QMap>
     
    template<typename KeyType>
    class QTableTrait
    {
    // Construction Destruction
    public:
    	QTableTrait( void );
    	~QTableTrait( void );
     
    // Types membres publics
    public:
    	typedef KeyType							QKeyType;
    	typedef QList<ClassName*>					QRowList;
    	typedef QMap<QKeyType,ClassName*>				QRowMap;
     
    // Fonctions membres publiques
    public:
    	//! Retourne la valeur de la clef dans la table pur cette instance
    	QKeyType	key						( void ) const;
     
    	//! Modifie la valeur de la clef dans la table pur cette instance
    	void		setKey						( const QKeyType &p_oKey );
     
     
    protected:
    	//! Valeur de la clef dans la table pour cette instance
    	QKeyType							m_oKey;
    };
     
    #include "TableTrait.cpp"
    J'utilise Qt4/VS2005 pour ce projet.
    Jusqu'ici pas de problème. L'utilisation se fait facilement.

    Là ou cela se corse pour moi, c'est que les classes qui utilisent cette template ont besoin d'initialiser leurs variables membres lors de l'instanciation. (elles sont initialisées une fois seulement lors de l'instanciation)

    Chaque classe a bien sûr des membres différents et certaines sont composées d'autres classes qui héritent aussi de QTableTrait.

    Ce que je veux faire c'est définir une structure de classes qui permette de
    - rendre commun l'interface de lecture des données pour toutes les classes (sachant que pour lire une base de données il faut quelques paramètres)
    - séparer la lecture des données de la classe de "stockage"
    - permettre facilement de changer de mode de lecture des données (passer d'une lecture SQL à une lecture d'un fichier XML par exemple)
    - prenne en compte qu'une classe peut etre composée d'autres classes du type QTableTrait et donc que ces sous classes doivent aussi etre initialisées

    Pour cela j'ai pensé utiliser le pattern "stratégie". Mais je ne vois pas comment le mettre en oeuvre dans mon cas....

    J'ai actuellement une architecture qui fonctionne mais qui n'est pas du tout adaptée a ce que je cherche à faire. Elle est basée sur une version modifiée de la template ci dessus, mais le code est un peu long. Si vous voulez y voir plus clair je peux le poster ici.

    Merci d'avance !

  2. #2
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2011
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Service public

    Informations forums :
    Inscription : Décembre 2011
    Messages : 54
    Points : 67
    Points
    67
    Par défaut
    Bonjour,

    Pour mieux comprendre ta problématique, quelques questions:

    Là ou cela se corse pour moi, c'est que les classes qui utilisent cette template ont besoin d'initialiser leurs variables membres lors de l'instanciation. (elles sont initialisées une fois seulement lors de l'instanciation)
    Pourquoi cela est-il un problèlme dans ton cas? Quelles difficultés as-tu rencontré?

    Pour cela j'ai pensé utiliser le pattern "stratégie". Mais je ne vois pas comment le mettre en oeuvre dans mon cas....
    Je ne connaissais pas ce pattern, mais en prenant le premier lien sur google (article wikipedia), c'est assez clair. Quel problème se pose dans ton cas?

    Je travaille sur un project où je vais également mettre en place une abstraction de la couche persistance, et je me suis tourné vers les concepts de DataAccessObject et DataTransferObject. Ceux-ci sont assez simples, voici une page explicative qui traite de ces deux concepts étroitement liés.

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2009
    Messages
    118
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 118
    Points : 27
    Points
    27
    Par défaut
    Citation Envoyé par vravier Voir le message
    Pourquoi cela est-il un problèlme dans ton cas? Quelles difficultés as-tu rencontré?
    He bien si j'ai bien compris, je dois faire une classe abstraite à la base de ma strategie, disons QAbstractParamReader
    puis pour lire les paramètre je peux spécialiser cette classe en SQLParamReader ou XmlParamReader.
    Jusqu'ici c'est bon
    Mais ce qui est mon sympa c'est que j'ai plein de classes qui doivent utiliser cette stratégie.
    Par exemple J'ai la classe QTrieur et QSortie. Alors dans ce cas il me faut deux implémentations de la lecture dans la base. Une pour le trieur, une autre pour les sorties. Si j'ai deux stratégies, il me faut alors quatre implémentations. C'est c'est un peu lourd mais c'est gérable.
    Disons que je fasse cela : j'ai alors TrieurSQLParamReader et SortieSQLParamReader

    Ce que je ne sais pas en revanche, c'est comment faire pour m'assurer que je passe la bonne stratégie à la bonne classe ??? Ou encore comment empêcher (si possible à la compilation) que l'on utilise SortieSQLParamReader sur QTrieur ???


    Citation Envoyé par vravier Voir le message
    Je travaille sur un project où je vais également mettre en place une abstraction de la couche persistance, et je me suis tourné vers les concepts de DataAccessObject et DataTransferObject. Ceux-ci sont assez simples, voici une page explicative qui traite de ces deux concepts étroitement liés.
    En effet cela parait se rapprocher de ce que je cherche a faire, je vais regarder cela aussi en parallèle...
    Merci

  4. #4
    Membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2011
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Service public

    Informations forums :
    Inscription : Décembre 2011
    Messages : 54
    Points : 67
    Points
    67
    Par défaut
    En effet, si tu as N objets métier, et M types de source différentes, tu auras NxM implémentations de classe gérant la persistance. Je n'ai pas vu de façon de faire qui évite ça. A un moment donné, il faut bien que le code soit spécifique à quelque chose. Les couches d'astraction sont là pour masquer tout ça, mais quelqu'un doit bien se taper le sale boulot

    Pour ce qui est de l'utilisation de la bonne implémentation de la classe gérant la persistance, en fait ton objet interrogera toujours une classe mère. Par exemple, QTrieur pointe sur un QTrieurAbstractParamReader, qui cache derrière un QTrieurSQLParamReader, ou un QTrieurXMLParamReader.

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2009
    Messages
    118
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 118
    Points : 27
    Points
    27
    Par défaut
    Bon si j'ai bien compris, j'obtiens une truc du genre de ça :



    Vous en pensez quoi ?

  6. #6
    Invité
    Invité(e)
    Par défaut
    salut,

    je pense qu'on peut considérer, que Qtrieur et Qsortie contiennent un reader.
    Donc avoir qqch comme
    Leur parent contient un Reader*

    Reader peut etre Xml ou SQL.
    Or, tu veux t'assurer que si tu utilises un Qtrieur tu utilises bien un reader de type Qtrieur.

    Bon je sais pas trop ce que font Qtrieur et Qsortie mais supposons qu'ils ont un parent Qparent...
    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
     
    #include <iostream>
    template<class T>
    class Reader{
    protected:
     Reader(){}
    };
     
    template<class T>
    class Parent{
      public:
      Reader<T>* reader;
    };
     
    class QTrieur:public Parent<QTrieur>{
     
    };
    class QSortie:public Parent<QSortie>{
     
    };
    class QTrieurReaderXml:public Reader<QTrieur>{
     
    };
    class QTrieurReaderSql:public Reader<QTrieur>{
     
    };
    class QSortieReaderXml:public Reader<QSortie>{
     
    };
    int main(int argc, char** argv)
    {
      QTrieur a;
      QTrieurReaderXml txml;
      QTrieurReaderSql tsql;
      QSortieReaderXml sxml;
      a.reader=&txml;
      a.reader=&sxml;//fails because sxml is of type Sortie and not Trieur
      return 0;
    }
    Au final tu as "effacé" la factory en fixant le type du reader (QTrieur ou QSortie) à la compilation.

    je passe outre les membres publics constructeurs parent et virtual destructors...

  7. #7
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2009
    Messages
    118
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 118
    Points : 27
    Points
    27
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    je pense qu'on peut considérer, que Qtrieur et Qsortie contiennent un reader.
    Donc avoir qqch comme
    Leur parent contient un Reader*

    Reader peut etre Xml ou SQL.
    Or, tu veux t'assurer que si tu utilises un Qtrieur tu utilises bien un reader de type Qtrieur.
    Bravo !

    C'est absolument ce que je cherche. Implémenté avec un CRTP en plus... pas mal. Il me semble aussi reconnaitre un pattern visiteur dans le reader..
    Bien joué.

    Il me reste une interrogation sur la responsabilité des classes. Si j'ai bien compris, cela devrait donner cela:

    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
     
    #include <iostream>
    template<class T>
    class Reader{
    public:
       virtual void read( T * ) = 0;
    protected:
     Reader(){}
    };
     
    template<class T>
    class Parent{
    protected:
       void read( void )
       {
          reader->read( this );
       }
      Reader<T>* reader;
    };
     
    class QTrieur:public Parent<QTrieur>{
    };
    class QSortie:public Parent<QSortie>{
     
    };
    class QTrieurReaderXml:public Reader<QTrieur>{
       virtual void read( T *pTrieur )
       {
    ... implementation du reader en xml avec le trieur
       };
     
    };
    class QTrieurReaderSql:public Reader<QTrieur>{
       virtual void read( T *pTrieur )
       {
    ... implementation du reader en sql avec le trieur
       }; 
    };
    class QSortieReaderXml:public Reader<QSortie>{
        virtual void read( T *pSortie )
       {
    ... implementation du reader en xml avec la sortie
       };
    };
     
    int main(int argc, char** argv)
    {
      QTrieur a;
      QTrieurReaderXml txml;
      QTrieurReaderSql tsql;
      QSortieReaderXml sxml;
      a.reader=&txml;
     
      a.read();
      return 0;
    }

  8. #8
    Invité
    Invité(e)
    Par défaut
    ben je sais pas trop ce que doivent faire tes classes.
    Je suis pas non plus la référence de la conception, tout ce que je peux dire, c'est qu'il vaut mieux choisir une conception qui repond à ce qu'on a définit (si il s'avère que c'est un pattern, alors c'est bon signe) qu'imbriquer des patterns qui ont tendance à complexifier le système.

    bref, je vois pas trop ce qui justifie de passer le trieur en argument du read par exemple. C'est à toi de définir ce que tu veux que fassent tes classes!

  9. #9
    Nouveau membre du Club
    Homme Profil pro
    Inscrit en
    Octobre 2009
    Messages
    118
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 118
    Points : 27
    Points
    27
    Par défaut
    Citation Envoyé par galerien69 Voir le message
    ben je sais pas trop ce que doivent faire tes classes.
    La classe Trieur est une classe d'interface avec un équipement industriel. C'est une classe abstraite de communication, l'implémentation étant faite dans un plugin.
    Actuellement le seul plugin implémenté comporte grosso modo une thread de gestion de messages TCP-IP.
    Mais la classe Trieur contient aussi des variables membre qui sont génériques à toutes les sortes de trieurs, quel que soit le plugin utilisé.
    Ces variables sont initialisées par lecture dans une base de données ou dans un fichier Xml (d'ou le reader)
    La classe sortie est juste une représentation des paramètres attachés à une sortie du trieur.
    Dans mon projet, j'ai en fait une quinzaine de classes qui sont en fait des représentation d'une ligne dans des tables. Le but des modification en cours, c'est que ces classes doivent être indépendantes de la base et que leur paramètres soient lus par une autre source.


    Citation Envoyé par galerien69 Voir le message
    bref, je vois pas trop ce qui justifie de passer le trieur en argument du read par exemple. C'est à toi de définir ce que tu veux que fassent tes classes!
    C'est pour cela que je parle de responsabilité des classes. J'ai très très souvent ce problème dans les projets :
    est ce que le trieur doit demander au reader ses paramètres ?
    ou bien est ce que le reader doit écrire ses paramètres dans le trieur ?

    En gros tu as deux solutions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main(int argc, char** argv)
    {
      QTrieur a;
      QTrieurReaderSql tsql;
     
      a.read( tsql );
      return 0;
    }
    ou
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main(int argc, char** argv)
    {
      QTrieur a;
      QTrieurReaderSql tsql;
     
      tsql.read( a );
      return 0;
    }

Discussions similaires

  1. Problème de conception : factory ou autre pattern?
    Par floctc dans le forum Langage
    Réponses: 2
    Dernier message: 13/04/2010, 16h38
  2. [Regexp] Remplacer un pattern dans un autre pattern ?
    Par titoumimi dans le forum Langage
    Réponses: 4
    Dernier message: 31/10/2006, 09h36
  3. [Conception] SELECT dépendant d'un autre (avec BDD)
    Par banzzai dans le forum PHP & Base de données
    Réponses: 5
    Dernier message: 07/08/2006, 17h57
  4. [Conception][Strategie] Combien de classes ?
    Par oceane751 dans le forum Général Java
    Réponses: 17
    Dernier message: 07/07/2005, 15h06
  5. Réponses: 2
    Dernier message: 07/10/2004, 16h31

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