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 :

Recherche du bon design pattern (ou pas)


Sujet :

C++

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2016
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2016
    Messages : 5
    Points : 1
    Points
    1
    Par défaut Recherche du bon design pattern (ou pas)
    Bonsoir à tous,

    voilà ma problématique :
    J'ai une classe mère M et plusieurs classes filles F1, F2, etc... Ces classes représentent les colonnes d'un tableau qui contient des types de données différents (double, string, etc..)

    Je récupère les données au fur et à mesure et je souhaite pouvoir modifier le type d'une colonne d'un type fille vers un autre type fille au fur et à mesure que j'analyse les données que je reçois. Si vous vous demandez "pourquoi ne pas le faire à la fin ?", la réponse est "pour gagner de l'espace mémoire", si je détecte qu'il s'agit d'entiers par exemple, je m'évite de les stocker sous forme de string.

    Bien sûr, la solution de base serait de créer à chaque fois une nouvelle instance fille du bon type, faire la copie puis supprimer l'ancienne... Néanmoins, c'est lourdingue et qui plus est, ce type de traitement doit être assez commun et j'ai l'impression qu'il doit surement y avoir un design pattern de derrière les fagots qui s'applique parfaitement à ce genre de problématique.

    Est-ce que quelqu'un a déjà été confronté à ce genre de problème ? Avez-vous des solutions ou des pistes à proposer ?

    Merci d'avance,

    d!

  2. #2
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Bonjour

    Un boost::variant (accepté pour C++17), associé au bon visteur, ne réglerait pas le souci ?

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2016
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2016
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Merci pour cette réponse. Elle ne résout pas mon problème, mais elle pourrait m'aider pour la suite.

    En fait, je ne veux pas seulement traiter la donnée que je reçois à la volée mais aussi la stocker (et donc potentiellement la reconvertir à la volée par la suite).

    Un exemple :
    Pour ma première colonne je lis une chaine de caractères correspondant à un entier comme première valeur => Je crée un objet ColonneDEntier qui contient un vector<int> et qui a certaines propriétés
    Pareil pour les dix valeurs suivantes => je continue de stocker dans mon vecteur d'entiers
    A la 12eme valeur, j'ai un nombre flottant => je veux convertir mon objet ColonneDEntier en ColonneDeFlottant qui contient un vector<float> et des propriétés différentes.

    Je sais que je n'échapperai pas à la copie (avec conversion) du contenu de vector<int> dans vector<float>, mais j'aimerai m'éviter la construction/destruction d'objets ColonneDeXxxx. Etant donné que ma classe qui stocke l'ensemble contient un vector<Colonne> (classe mère), je voudrais éviter d'avoir à lui faire gérer plein de vérifications de types pour savoir quels sont les new spécifiques qu'il doit faire.

    Si je schématise l'algo, j'ai quelque chose dans ce goût la :

    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
     
    vector<Colonne *> monTableau;
     
    nbColonnes = lirePremierligne(); //La premiere ligne me permet juste de définir le nombre de colonnes, je ne traite pas son contenu
    monTableau.resize(nbColonnes);
    for (int i = 0; i < nbColonnes; ++i)
         monTableau[i] = new ColonneDeBooleens();   //J'initialise avec Booleen car c'est le type le plus restrictif dans mon cas
     
    lireChaqueLigne()
         pourChaqueValeurDeLaLigne()
              monTableau[i]->ajouteElement(valeur);  //La méthode ajoute élément vérifie si 'valeur' correspond bien au type courant de la colonne
     
              // Le cas échéant, je pourrais imaginer avoir ici quelque chose qui récupère le nouveau type et qui fait un truc du genre....
              switch (nouveauTypeColonne)
              {
                   case TypeTruc : ColonneDeTruc *nouvelleCol = new ColonneDeTruc(monTableau[i]); // Avec du coup, un constructeur par recopie de chaque type vers chaque type...
                        monTableau.remove(i);
                        monTableau.insert(i, nouvelleCol);
                        break;
                   case ... // Pareil pour chaque type
              }
              // ... Mais je voudrais éviter parce que je trouve ça crado... ^^
     
    Voilà, j'espère que c'est un peu plus clair.
     
    Merci pour vos réponses et suggestions. :)
     
    d!

  4. #4
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 045
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 045
    Points : 11 368
    Points
    11 368
    Billets dans le blog
    10
    Par défaut
    Tu lis une première ligne, qui te permet de déterminer le type de chaque colonne?
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  5. #5
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2016
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2016
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Citation Envoyé par dragonjoker59 Voir le message
    Tu lis une première ligne, qui te permet de déterminer le type de chaque colonne?
    Non, je choisi le type de façon arbitraire au départ.

  6. #6
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    C'est un exercice intéressant.

    Proposition :
    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
    class ColonneAbstraite
    {
    public:
        virtual ~ColonneAbstraite() {}
        virtual void ajouteElement(Tableau& parent, size_t indiceDeMoiMemeDansParent, const std::string& valeur) = 0;
            // Si la valeur n'a pas le bon type, créer une nouvelle colonne,
            // l'insérer dans le tableau à notre place et se suicider.
        // ...
    };
     
    class Tableau
    {
    private:
        std::vector<std::unique_ptr<ColonneAbstraite>> m_colonnes;
    public:
        std::unique_ptr<ColonneAbstraite> libererColonne(size_t indice) {
            assert(indice < m_colonnes.size());
            return std::move(m_colonnes[indice]);
        }
        void setColonne(size_t indiceColonne, std::unique_ptr<ColonneAbstraite> nouvelleColonne) {
            assert(indiceColonne < m_colonnes.size());
            m_colonnes[indiceColonne] = std::move(nouvelleColonne);
        }
        void ajouteElement(size_t indiceColonne, const std::string& valeur) {
            assert(indiceColonne < m_colonnes.size());
            m_colonnes[indiceColonne]->ajouteElement(*this, indiceColonne, valeur);
        }
        // ...
    };
     
    class ColonneChaines : public ColonneAbstraite { /* ... */ };
     
    class ColonneEntiers : public ColonneAbstraite { /* ... */ };
     
    class ColonneBooleens : public ColonneAbstraite
    {
    private:
        std::vector<bool> m_cellules;
    public:
        virtual ~ColonneBooleens() {}
        void ajouteElement(Tableau& parent, size_t indiceDeMoiMemeDansParent, const std::string& valeur) override
        {
            boost::optional<bool> valeurBooleenne = convertirEnBooleen(valeur);
            if(valeurBooleenne) {
                m_cellules.push_back(*valeurBooleenne);
            } else {
                std::unique_ptr<ColonneAbstraite> nouvelleColonne;
                boost::optional<int> valeurEntiere = convertirEnEntier(valeur);
                if(valeurEntiere) {
                    std::unique_ptr<ColonneEntiers> nouvelleColonneEntiers(new ColonneEntiers(*this));
                    nouvelleColonneEntiers->ajouterEntier(*valeurEntiere);
                    nouvelleColonne = std::move(nouvelleColonneEntiers);
                } else {
                    std::unique_ptr<ColonneChaines> nouvelleColonneChaines(new ColonneChaines(*this));
                    nouvelleColonneChaines->ajouterChaine(valeur);
                    nouvelleColonne = std::move(nouvelleColonneChaines);
                }
                std::unique_ptr<ColonneAbstraite> moi = parent.libererColonne(indiceDeMoiMemeDansParent);
                assert(moi.get() == this);
                parent.setColonne(indiceDeMoiMemeDansParent, std::move(nouvelleColonne));
            }
        }
        // ...
    };

  7. #7
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    J'ai essayé avec un variant et semble me semble plus simple avec moins de risque d'oublier une conversion: cela ne compilerait pas (ex std::vector<int> -> std::vector<std::string>).

    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
    #include <boost/variant.hpp>
    #include <type_traits>
    #include <vector>
     
    struct Colonne
    {
      template<class T>
      void ajouteElement(T x) {
        boost::apply_visitor([&x, this](auto & vec) {
          this->ajouteElement_impl(vec, x);
        }, variant);
      }
     
      template<class Visitor>
      void apply(Visitor v) const {
        boost::apply_visitor(v, variant);
      }
     
    private:
      template<class T>
      void ajouteElement_impl(std::vector<T> & vec, T x) {
        vec.push_back(x);
      }
     
      template<class T, class U>
      std::enable_if_t<std::is_arithmetic<T>{} && std::is_arithmetic<U>{}>
      ajouteElement_impl(std::vector<T> & vec, U x) {
        std::vector<U> new_vec(begin(vec), end(vec));
        new_vec.push_back(x);
        variant = std::move(new_vec);
      }
     
      boost::variant<std::vector<bool>, std::vector<float>, std::vector<int>> variant;
    };
     
     
    #include <iostream>
     
    char const * get_type(std::vector<float> const &) { return "vector<float>"; }
    char const * get_type(std::vector<bool> const &) { return "vector<bool>"; }
    char const * get_type(std::vector<int> const &) { return "vector<int>"; }
     
    std::ostream & operator << (std::ostream & out, Colonne const & colonne) {
      colonne.apply([&out](auto & vec) {
        out << get_type(vec) << ": ";
        for (auto && x : vec) {
          out << x << ", ";
        }
      });
      return out;
    }
     
    int main() {
     
    Colonne colonne;
    colonne.ajouteElement(1); // int
    std::cout << colonne << "\n";
    colonne.ajouteElement(1.4f); // float
    std::cout << colonne << "\n";
    colonne.ajouteElement(0); // int
    std::cout << colonne << "\n";
    colonne.ajouteElement(true); // bool
    std::cout << colonne << "\n";
    colonne.ajouteElement(false); // bool
    std::cout << colonne << "\n";
     
    }
    Résultat:
    vector<int>: 1,
    vector<float>: 1, 1.4,
    vector<int>: 1, 1, 0,
    vector<bool>: 1, 1, 0, 1,
    vector<bool>: 1, 1, 0, 1, 0,

  8. #8
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 469
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 469
    Points : 6 102
    Points
    6 102
    Par défaut
    Citation Envoyé par jo_link_noir Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
      template<class T, class U>
      std::enable_if_t<std::is_arithmetic<T>{} && std::is_arithmetic<U>{}>
      ajouteElement_impl(std::vector<T> & vec, U x) {
        std::vector<U> new_vec(begin(vec), end(vec));
        new_vec.push_back(x);
        variant = std::move(new_vec);
      }
    Citation Envoyé par jo_link_noir Voir le message
    Résultat:
    vector<int>: 1,
    vector<float>: 1, 1.4,
    vector<int>: 1, 1, 0,
    vector<bool>: 1, 1, 0, 1,
    vector<bool>: 1, 1, 0, 1, 0,
    Je crois que ça ne correspond pas au besoin de d000x.
    De ce que j'ai compris, quand il a une colonne avec des éléments de type T et qu'on ajoute un élément de type U, la colonne doit avoir des éléments de type max(T,U) selon la relation d'ordre :
    bool < int < double < std::string.
    Par contre, dans ce que tu as codé, la colonne se retrouve toujours avec des éléments de type U.

    Il faudrait complexifier le code en utilisant quelque chose dans ce genre-là :
    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
    template<class T, class U>
    struct pairTypes
    {
    };
     
    template<> struct pairTypes<std::string, int> { typedef std::string typeEnglobant; };
    template<> struct pairTypes<bool, int>        { typedef int         typeEnglobant; };
    // autres spécialisations...
     
    template<class T, class U>
    T convertirVers(const U& x) {
        return x;
    }
     
    template<> std::string convertirVers<std::string>(const int& x) { return std::to_string(x); }
    // autres spécialisations...
     
    template<class T, class U>
    typename pairTypes<T, U>::typeEnglobant
    convertirVersTypeEnglobant(U x) {
        return convertirVers<typename pairTypes<T, U>::typeEnglobant>(x);
    }

  9. #9
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2016
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2016
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Bonjour,

    tout d'abord, merci à vous deux pour vos réponses.

    Citation Envoyé par Pyramidev Voir le message
    Je crois que ça ne correspond pas au besoin de d000x.
    De ce que j'ai compris, quand il a une colonne avec des éléments de type T et qu'on ajoute un élément de type U, la colonne doit avoir des éléments de type max(T,U) selon la relation d'ordre :
    bool < int < double < std::string.
    Par contre, dans ce que tu as codé, la colonne se retrouve toujours avec des éléments de type U.
    en effet! Qui plus est pour des raisons techniques, je ne peux pas utiliser de templates ici.

    La solution de Pyramidev correspond plus à mon besoin. Je n'avais pas vu la réponse plus tôt, du coup en attendant j'ai opté pour une solution moins "propre", je vous la livre ici purement à titre d'info :

    Je n'ai plus qu'une seule classe de Colonne indéfinie. Cette colonne contient tous les types (je pars du principe qu'un vector<> vide ne coute pas beaucoup de place de stockage...). Du coup, lors d'un changement de type, j'ai juste à transférer les données d'un vector à un autre sans avoir à me soucier de l'objet Colonne en lui même qui ne bouge pas.

    Ci-dessous une version "sale et simplifiée" de ce que ça peut donner...

    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
     
     
    	void Colonne::addElement(const std::string &element)
    	{
                    checkType(element);
    		switch (_type)
    		{
    		case booleen:
    			_boolValues[_curElem++] = element;
    			break;
    		case float:
    			_floatValues[_curElem++] = atof(element.c_str());
    			break;
    		case int:
    			_intValues[_curElem++] = atoi(element.c_str());
    			break;
    		}
    	}
     
     
            void Colonne::checkType(const string &element) {
    		if (_type == bool && !isBoolean(element))
    		{
    			if (!isInteger(element))
                            {
                                    _type = float;
    			        convertFromBoolToFloat();
                            }
    			else
                            {
                                    _type = int;
    				convertFromBoolToInt();
    			}
    		}
    		if (_type == int && !isInteger(element))
                    {
                            _type = float;
    		        convertFromIntToFloat();
    		}
    	}
     
     
            void Colonne::convertFromIntToFloat()
    	{
    		_floatValues.resize(_nbElem);
     
    		for (size_t count = 0; count < _curElem; ++count)
    			_floatValues[count] = _intValues[count];
     
    		_intValues.clear();
    	}
     
            void Colonne::convertFromBoolToFloat()
            { }
            void Colonne::convertFromBoolToInt()
            { }
     
             /* .... */
    La solution de Pyramidev me paraît très bien, je vais gratter dans ce filon.

    Merci encore !

    d!

  10. #10
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par d000x Voir le message
    Qui plus est pour des raisons techniques, je ne peux pas utiliser de templates ici.
    Ça ne doit pas être tout à fait les vraies contraintes car std::vector et std::string sont templates

  11. #11
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2016
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2016
    Messages : 5
    Points : 1
    Points
    1
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    Ça ne doit pas être tout à fait les vraies contraintes car std::vector et std::string sont templates
    Je ne voulais pas dire que je ne pouvais pas utiliser de classe template, juste que je ne pouvais pas "templater" mes propres classes

Discussions similaires

  1. Le bon design pattern.
    Par Ali Kent dans le forum UML
    Réponses: 2
    Dernier message: 10/01/2015, 15h49
  2. [Couplage] Bon design pattern pour synchroniser deux modules (Adapter, Observer ?)
    Par Steph0 dans le forum Design Patterns
    Réponses: 2
    Dernier message: 21/06/2013, 14h16
  3. [PHP 5.1] A la recherche d'un design pattern
    Par Invité dans le forum Langage
    Réponses: 1
    Dernier message: 27/08/2010, 13h35
  4. [Composite] Recherche d'un design pattern proche
    Par telluri dans le forum Design Patterns
    Réponses: 7
    Dernier message: 03/04/2010, 17h21
  5. Design Pattern ou pas ?
    Par jc63 dans le forum Design Patterns
    Réponses: 3
    Dernier message: 13/08/2009, 23h22

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