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 :

sémantique et rapport de classe


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 293
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 293
    Billets dans le blog
    2
    Par défaut sémantique et rapport de classe
    Bonjour,

    j'ai une question très peu précise qui me taraude depuis quelques jours, et j'aurais aimé avoir d'autres avis que le mien.

    Nous avons une classe Data, qui contient quelques données. Pas beaucoup: disons 4 variables membres.
    Cette classe a une sémantique de valeur; elle va être stockée dans des conteneurs, triée, copiée, etc.
    Elle sera donc dotée du big 5 (dtor, default ctor, copy ctor, move ctor et operator =).

    Maintenant voici mon problème. Cette classe Data va être instanciée et construite de diverses façons. Elle peut être construite à partir de trames reçue par le module réseau, à partir de données récupérées par le gestionnaire de configuration, via l'interface d'accès aux bases de données, etc.
    Mon interrogation consiste à trouver la meilleure façon de gérer ces multiples façons de créer mon objet. La première tentation consiste à passer par une factory. Mais étant donné que mon objet est petit et qu'il n'évoluera pas, j'ai l'impression que de passer par une factory c'est un peu le "bulldozer pour planter une fleur", si vous voyez ce que je veux dire.
    Alors je réfléchissais à ajouter des fonctions membres à ma classe Data, du style:
    void CreateFromRecord( const DataBaseRecord & record );
    void CreateFromFile( ifstream & file );
    // etc.

    Cette solution me plait plus, car ça simplifie mon design, ça fait une classe en moins etc, bref, ça simplifie la maintenance. Mais je ne sais pas, j'ai le sentiment qu'il y a quelque chose qui ne va pas avec cette façon de faire. C'est pourquoi je voulais avoir votre avis, vos réflexions.

  2. #2
    Membre chevronné
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Par défaut
    Je suppose que tu y as pensé, mais en faisant ça tu rends ta classe Data dépendante d'un tas d'autres trucs comme DataBaseRecord, ce que tu n'as pas forcément envie de faire.
    De plus, ajouter des fonctions membres rend l'objet Data plus complexe à maintenir. Est-ce que CreateFromRecord() en tant que fonction membre peut garantir des invariants sur Data qu'une fonction non-membre ne pourrait pas garantir ?

    Ensuite, je ne connais pas ton cas, mais il y a peut-être une façon "presque naturelle" de créer un objet Data à partir d'une séquence d'éléments. Si c'est le cas, alors pourquoi ne pas proposer constructeur template dans le style de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    template<typename It> Data::Data (It begin, It end) // Construit l'objet à partir d'une séquence
    Et avoir des fonctions du style CreateFromRecord () qui appellent ce constructeur à partir d'une séquence qu'elles ont construites ?

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

    As-tu pensé au fait que, quelle que soit l'origine des valeurs que tu voudras donner aux membres de ta classe, ce que tu veux réellement faire n'est jamais que de la "dé-sérialisation"

    Et, le fait est que la récupération de ces valeurs est, de toutes manières, dépendante de leur origine.

    Comme je présume que tu as bien fait les choses et que tu as d'office un constructeur qui prend comme attribut les valeurs des quatre membre, pourquoi vouloir se mettre martel en tête Chaque système de "dé-sérialisation" peut avoir une fonction spécifique qui crée la donnée selon ses propres spécificités et "basta"
    [EDIT]
    Au pire, rien ne t'empêche de créer une interface template (utilisée par CRTP) 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
    template <typename CRTP>
    class IDataCreator{
        Data createData() ;
    protected:
        IDataCreator(CRTP & real):real_(real){}
        ~IDataCreator(){}
    private
    CRTP & real_;
    };
    class DBaseReader : public IDataCreator<DBaseReader>{
        friend class IDataCreator<DBaseReader>;
        /* ... */
    };
    et de fournir une spécialisation totale de create data pour chaque contexte spécifique, mais le résultat sera identique (si ce n'est que le code indiquera explicitement que les classes dérivées sont effectivement susceptibles de créer la donnée )
    [/EDIT]
    @phil1981 Ton code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template<typename It> Data::Data (It begin, It end) // Construit l'objet à partir d'une séquence
    présente un inconvénient majeur : celui d'impliquer que les quatre membres soient de type identique, à moins de passer par un boost::variant ou similaire.

    Ce n'est pas forcément garanti (en tout cas, r0d n'a rien dit à ce sujet) et la solution d'un variant (qu'il soit de boost ou de Qt ou de n'importe quelle autre origine) est peut etre aussi un peu overkill pour une donnée du genre
    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

  4. #4
    Membre chevronné
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Par défaut
    Citation Envoyé par koala01 Voir le message
    @phil1981 Ton code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template<typename It> Data::Data (It begin, It end) // Construit l'objet à partir d'une séquence
    présente un inconvénient majeur : celui d'impliquer que les quatre membres soient de type identique, à moins de passer par un boost::variant ou similaire.
    Je crois pas me tromper en disant "pas forcément". Il faut que les quatre membres puissent être convertis depuis une séquence d'éléments du même type. Un exemple : si mes 4 membres sont respectivements de type "float", "int", "double" et pourquoi pas "Date" (un type utilisateur), alors pourquoi ne pas passer en argument un vecteur de 4 "std::string", si tu as des moyens de convertir depuis une chaîne de caractères ?
    Je t'accorde que c'est un peu "overkill", toutefois.
    Mon idée c'était surtout de démontrer que le constructeur devrait être indépendant de la source des données. Comme tu l'exprimes, un constructeur pourrait prendre les 4 membres en paramètre (ou en déduire certains d'entre eux, si possible), et laisser à une fonction de déserialisation le soin d'appeler ce constructeur.

  5. #5
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2010
    Messages
    434
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2010
    Messages : 434
    Par défaut
    Bonjour,

    Si tu a des types de paramètres différents en fonction de l'entrée tu peux toujours utiliser la surcharge.
    Sinon le plus propre est de passer par un factory methode et dans l'idéal avec une interface comme ça tu à du couplage faible.

    Ensuite si tu a un objet différent en fonction de ton entrée tu peux toujours faire une surcharge de l'opérateur =.

    Il manque pas mal d'info pour voir réelement qu'elle est la meilleur manière de faire, etant donné que la meilleur solution dépend du contexte dans sa globalité

  6. #6
    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 phi1981 Voir le message
    Je crois pas me tromper en disant "pas forcément". Il faut que les quatre membres puissent être convertis depuis une séquence d'éléments du même type. Un exemple : si mes 4 membres sont respectivements de type "float", "int", "double" et pourquoi pas "Date" (un type utilisateur), alors pourquoi ne pas passer en argument un vecteur de 4 "std::string", si tu as des moyens de convertir depuis une chaîne de caractères ?
    Je t'accorde que c'est un peu "overkill", toutefois.
    Mon idée c'était surtout de démontrer que le constructeur devrait être indépendant de la source des données. Comme tu l'exprimes, un constructeur pourrait prendre les 4 membres en paramètre (ou en déduire certains d'entre eux, si possible), et laisser à une fonction de déserialisation le soin d'appeler ce constructeur.
    A priori, le comportement que tu décris là n'est surtout pas le rôle d'un constructeur

    Maintenant, je t'accorde que tu pourrais avoir une fonction libre pourrait faire l'affaire. Elle serait 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
    template <typename iterator>
    Data create(iterator begin, iterator end){ // reçoit d'office des itérateurs vers des std::string
        std::stringstream ss;
        while(begin != end){
            ss<<*begin;
            ++begin;
        }
       int first;
       float second;
       double third;
       std::string fourth;
       ss>>first>>second>>third>>fourth;
       return Data(first,second,third,fourh);
    }
    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

  7. #7
    Membre chevronné
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Par défaut
    Citation Envoyé par koala01 Voir le message
    A priori, le comportement que tu décris là n'est surtout pas le rôle d'un constructeur
    Pas faux, puisque effectivement, une fonction libre pourrait faire l'affaire. Merci d'avoir clarifié.

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

Discussions similaires

  1. Réponses: 20
    Dernier message: 09/04/2020, 17h02
  2. Réponses: 5
    Dernier message: 01/04/2008, 21h58
  3. Réponses: 3
    Dernier message: 22/06/2007, 22h14
  4. Changer le .class (dont le rapport se sert) à chaud
    Par tiboudchou dans le forum BIRT
    Réponses: 1
    Dernier message: 11/07/2006, 09h23
  5. Réponses: 18
    Dernier message: 08/04/2006, 10h39

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