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

Téléchargez C++ Discussion :

Object factory


Sujet :

Téléchargez C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    869
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 869
    Par défaut Object factory
    Bonjour,

    Je vous propose un nouvel élément à utiliser : Object factory

    La sortie de la norme C++11 nous a ouvert pas mal d'horizons. J'ai donc cree une classe ObjectFactory qui permet grace aux templates variadiques de creer n'importe quel type d'objet. N'hesitez pas a me donner vos avis.

    Qu'en pensez-vous ?

    PS: koala01 a poste une alternative pour faire une ObjectFactory ==> ici <==.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Salut,

    Ben, perso, je ne suis pas convaincu...

    La raison va te paraître idiote mais j'ai du mal à imaginer devoir créer une collection de fabriques pour être en mesure de créer mes objets...

    Généralement, tu as une seule fabrique qui s'occupe de créer les différents types d'objets dérivant d'un type de base, mais ici, tu te retrouve avec autant de fabriques que de type dérivés Tu perds littéralement tout l'attrait du patron factory (car, l'idée, c'est que tu ne doive pas t'inquiéter du type réel de l'objet créé par la fabrique.

    Or, du simple fait que tu es obligé de créer une fabrique par type d'objet à créer, tu te trouves bel et bien face à la nécessité de connaître tous les types d'objets qui seront créés.

    Tu vois où je veux en venir
    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 imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    869
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 869
    Par défaut
    Je dois admettre avoir un peu de mal a te suivre... On peut templater la classe ObjectFactory et du coup permettre d'avoir une seule et unique factory, le but etant juste de demontrer comment faire une ObjectFactory en C++11. En tout cas je ne cracherais pas sur une explication un peu plus complete de ce qui te pose probleme.

    En ce qui me concerne, je m'en sers pour stocker tous les objets de mon moteur graphique. Du coup ca me permet de faire une page de chargement en stockant le type de l'objet a creer et de l'initialiser plus tard.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    Le but de la fabrique est que son utilisateur puisse se "contenter" de basarder les informations dont elle a besoin pour créer les objets qu'on lui demande sans avoir à s'inquiéter du type réel de l'objet qu'il (l'utilisateur) obtiendra.

    En gros, en orienté objet pur, la fabrique de ton exemple ressemblerait à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Factory{
        /* renvoie un pointeur sur un objet de type MotherClass
         * qui est réellement un objet de type MotherClass
         */
        MotherClass * create();
        /* renvoie un pointeur sur un objet de type MotherClass
         * qui est en réalité un objet de type DaughterClass1
         */
        MotherClass * create( int);
        /* renvoie un pointeur sur un objet de type MotherClass
         * qui est en réalité un objet de type DaughterClass2
         */
        MotherClass * create( char *, float );
    };
    (je fais simple et je ne m'intéresse pas trop aux détails )

    Cette fabrique pourrait être utilisée sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main(){
        /* tant qu'à faire, utilisons les pointeurs intelligents :D */
        std::vector<std::unique_ptr<MotherClass>> datas;
        Factory factory;
        data.emplace_back(std::unique_ptr<MotherClass>(factory.create());
        data.emplace_back(std::unique_ptr<MotherClass>(factory.create(5));
        data.emplace_back(std::unique_ptr<MotherClass>(factory.create("hello", 3.1415926));
        /* ... */
    }
    Or, je n'ai regardé le code que de manière assez distraite, mais ce qui me pose problème, c'est la nécessité d'avoir un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     std::vector<ObjectFactory*>	vec;
    vec.push_back(ObjectFactory::createNewObject<DaughterClass1, int>(18));
    vec.push_back(ObjectFactory::createNewObject<MotherClass>());
    vec.push_back(ObjectFactory::createNewObject<DaughterClass2, const char*, float>("test", 4.f));
    ou, du moins, de devoir préciser non seulement le type d'objet que tu veux que ta fabrique crée, mais aussi les paramètres qu'elle devra accepter.

    Autrement dit, tu nous explique que tu as une fabrique qui devrait s'utiliser sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main(){
        std::vector<MotherClass *> datas;
        datas.push_back(ObjectFactory::createNewObject<MotherClass>()->createObject());
        datas.push_back(ObjectFactory::createNewObject<DaughterClass1, int>( 18)->createObject());
        datas.push_back(ObjectFactory::createNewObject<DaughterClass2, const char*, float>("hello", 3.1415926)->createObject());
    }
    (free style, je n'ai pas testé )Bien sur, je simplifie (parce qu'il y a le problème de la fuite mémoire que ce code engendre ).

    Dés lors, je te pose la question : Si tu dois connaître exactement le type de l'objet réel pour lequel tu veux récupérer le pointeur, pourquoi te faire ch..er à passer par une fabrique alors que tu aurais tout aussi bien pu directement créer ton objet au travers de new

    Bon, d'accord, tu me diras que tu peux te contenter d'avoir une déclaration anticipée dans l'histoire et que tu peux aller planquer l'inclusion du fichier d'en-tête des classes dérivées dans un quelconque fichier xxx_explicit.cpp, mais j'ai quand même l'impression que tu te fais du mal pour pas grand chose

    Attention, du pur point de vue de la technique pure, je n'ai pas grand chose à redire sur l'implémentation (que je n'ai regardé que d'un œil distrait ), mais une belle technique inutile reste toujours inutile aussi poussée soit elle
    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

  5. #5
    Membre Expert
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    869
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 869
    Par défaut
    Non, je crois que tu as bien cerne le tout. A l'origine j'avais justement une classe avec des methodes qui ressemblaient a ce que tu as montre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Factory{
        MotherClass * create();
        MotherClass * create( int);
        MotherClass * create( char *, float );
    };
    Le probleme c'est que le nombre d'objet possedant chacun leur propre constructeur heritant tous d'une meme classe a tres vite augmente (chacun de ses objets avait bien evidemment plusieurs constructeurs). Sans parler du fait que certains avait exactement le meme constructeur, comment differencier mon Cube de ma Box (ceci est un exemple bidon ) ?
    J'ai donc tout simplement cherche a contourner le probleme mais je ne savais pas comment stocker les arguments dans ma classe sans m'obliger a repasser par quelque chose comme ca. C'est donc de la que vient la creation de cette classe ObjectFactory.

    Par pure curiosite, comment aurais-tu resolu mon probleme ? Je l'avais fait de cette facon :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class ObjectFactory
    {
    public:
      static ObjectFactory *cube(arg1, arg2);
      static ObjectFactory *sphere(arg1, arg2, arg3, arg4);
      static ObjectFactory *model(arg1);
      ...
      Object *create();
     
    private:
      arg1  a1;
      arg2  a2;
      ...
    };
    Je ne sais pas si mon code exemple est tres clair sur ce que j'ai fait.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 635
    Par défaut
    J'y réfléchis à peu près en même temps que je n'écris cette réponse, donc, il y aura sans doute quelques arrangements à faire

    Mais déjà, j'aurais commencé par m'assurer qu'il n'y a qu'un seul constructeur non trivial par objet concret à créer.

    Ensuite, je crois que j'aurais travaillé de la sorte.

    J'aurais déjà une structure qui me permet d'empêcher la copie et l'affectation des objet ayant sémantique d'entité sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    struct NonCopyable{
        NonCopyable(NonCopyable const &) = delete;
        NonCopyable operator=(NonCopyable const &) = delete;
    protected:
        NonCopyable(){}
        ~NonCopyable(){}
    };
    (oui, je sais, c'est très ressemblant à boost, hein )
    et, pour une hiérarchie de classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Base : private NonCopyable
    {
        public:
            Base();
            virtual ~Base();
            virtual void print() const = 0;
        protected:
        private:
    };
    class Derivee1 : public Base
    {
        public:
            Derivee1(int i);
            virtual ~Derivee1();
            void print() const;
        protected:
        private:
            int i;
    };
    class Derivee2 : public Base
    {
        public:
            Derivee2(std::string const & str);
            virtual ~Derivee2();
            void print() const;
        protected:
        private:
            std::string str;
    };
    /* .. */
    class DeriveeN : public Base
    {
        public:
            DeriveeN(std::string const & str, double d);
            virtual ~DeriveeN();
            void print() const;
        protected:
        private:
            std::string str;
            double d;
    };
    j'aurais créé un trait pour chaque type concret qu'il me faudra créer. cela aurait pris la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    struct d1_trait{};
    struct d2_trait{};
    /* ... */
    struct dN_trait{};
    Ensuite, j'aurais créé une structure de base qui aurait représenté une liste de paramètre et j'y aurais adjoint la déclaration anticipée d'une classe template, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct ParameterList : private NonCopyable
    {
        public:
        protected:
            ParameterList();
            ~ParameterList();
        private:
    };
    template <typename Trait>
    struct ConcreteParameterList;
    Et j'aurais fournis des spécialisations de ConcreteParameterList pour chaque trait, 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
    template <>
    struct ConcreteParameterList<d1_trait> : public ParameterList{
        int i;
    };
    template <>
    struct ConcreteParameterList<d2_trait> : public ParameterList{
        std::string str;
    };
    /* ... */
    template <>
    struct ConcreteParameterList<dN_trait> : public ParameterList{
        std::string str;
        double d;
    };
    Et j'aurais créé une classe template pour la création des objets dérivés de base sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Base;
    class ParameterList;
    template <typename Type>
    struct DerivedCreator{
        Base * create(ParameterList const &) const;
    };
    J'aurais terminé en fournissant la fabrique, qui disposerait d'une surcharge de la fonction create pour pour chaque trait envisagé et qui recevrait également une ParameterList, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Base;
    class ParameterList;
    class Factory
    {
        public:
            Factory();
            Base * create(d1_trait const &, ParameterList const &) const;
            Base * create(d2_trait const &, ParameterList const &) const;
            /* ... */
            Base * create(dN_trait const &, ParameterList const &) const;
        protected:
        private:
    };
    Et, pour finir, dans le fichier d'implémentation de la factory, j'aurais fourni une spécialisation totale de la fonction Base * DerivedCreator::create(ParameterList const &) const; en même temps que l'implémentation de la fonction create de la fabrique, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    template <>
    Base * DerivedCreator<d1_trait>::create(ParameterList const & pl) const{
        ConcreteParameterList<d1_trait> const & temp =
                static_cast<ConcreteParameterList<d1_trait> const & >(pl);
         return new Derivee1(temp.i);
    }
    Base * Factory::create(d1_trait const &, ParameterList const & pl) const{
        return DerivedCreator<d1_trait>()::create(pl);
    }
    template <>
    Base * DerivedCreator<d2_trait>::create(ParameterList const & pl) const{
        ConcreteParameterList<d2_trait> const & temp =
                static_cast<ConcreteParameterList<d2_trait> const & >(pl);
         return new Derivee2(temp.str);
    }
     
    Base * Factory::create(d2_trait const &, ParameterList const & pl) const{
        return DerivedCreator<d2_trait>()::create(pl);
    }
    /* ... */
    /* ... */
    template <>
    Base * DerivedCreator<dN_trait>::create(ParameterList const & pl) const{
        ConcreteParameterList<dN_trait> const & temp =
                static_cast<ConcreteParameterList<dN_trait> const & >(pl);
         return new DeriveeN(temp.str, temp.d);
    }
    Base * Factory::create(dN_trait const &, ParameterList const & pl) const{
        return DerivedCreator<dN_trait>()::create(pl);
    }
    Je sais, cela t'oblige à fournir deux spécialisations à chaque fois que tu veux rajouter un type dérivé de Base, mais d'un autre coté, tu ne dévoile absolument plus rien des types dérivés de Base à l'utilisateur de ta fabrique.

    Tout ce qu'il connaîtra, c'est le type Base, une série de traits qui lui permettent de définir le type réel et les arguments qu'il doit passer

    Le tout pourrait être utilisé sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include <Base.h> // la classe Base
    #include <D1Params.h> // les paramètres pour Derivee1
    #include <D2Params.h> // les paramètres pour Derivee2
    /* ... */
    #include <DNParams.h>// les paramètres pour DeriveeN
    #include <Factory.h>  // la fabrique
     
    int main()
    {
        ConcreteParameterList<d1_trait> pl1;
        pl1.i = 12;
        ConcreteParameterList<d2_trait> pl2;
        pl2.str = "salut";
        /* ... */
        ConcreteParameterList<dN_trait> plN;
        plN.str = "hello";
        plN.d= 3.1415926;
        Factory factory;
        Base * ptr1 = factory.create(d1_trait(),pl1);
        Base * ptr2 = factory.create(d2_trait(),pl2);
        /* ... */
        Base * ptrN = factory.create(dN_trait(),plN);
        ptr1->print();
        ptr2->print();
        /* ... */
        ptrN->print();
        /* ... */
       delete ptr1;
        delete ptr2;
        /* ... */
        delete ptrN;
        return 0;
    }
    PS: bon, je sais, j'ai peut être été un peu loin en utilisant un DerivedCreator vu que, tant qu'à surcharger la fonction create de ma fabrique, j'aurais tout aussi bien pu faire la conversion directement à l'intérieur de celle-ci...

    Mais cette indirection supplémentaire pourrait peut être te mettre sur la voir d'une amélioration réelle
    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. object factory et classes templates
    Par [Hugo] dans le forum C++
    Réponses: 11
    Dernier message: 03/01/2012, 17h33
  2. [JAXB] Génerer un object factory a partir d'un schema xml
    Par bel09 dans le forum Persistance des données
    Réponses: 0
    Dernier message: 02/06/2009, 16h24
  3. template, singleton et object factory
    Par [Hugo] dans le forum Langage
    Réponses: 13
    Dernier message: 04/05/2009, 13h53
  4. [Data Access Object]Intérêt de la factory ?
    Par le Daoud dans le forum Général Java
    Réponses: 2
    Dernier message: 21/04/2005, 09h06
  5. Comment inserer des donnee de type Large Object !!
    Par josoft dans le forum Requêtes
    Réponses: 4
    Dernier message: 20/07/2003, 11h21

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