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 :

[C++] Demande précision sur Pattern Factory


Sujet :

C++

  1. #1
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut [C++] Demande précision sur Pattern Factory
    Bonsoir,

    Je n'arrive pas à comprendre l'intérêt du pattern Factory par rapport au polymorphisme.

    Voilà deux codes qui font la même chose :

    1ère version

    main.cpp
    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
    #include <iostream>
    #include "factory.h"
     
    using namespace std;
     
    int main()
    {
        Fabrique* factory = new Fabrique();
     
        if (factory)
        {
            Form* f1 = factory->create("cercle");
            Form* f2 = factory->create("carree");
            f1->Dessiner();
            f2->Dessiner();
     
            delete f1, f2, factory;
        }
     
        return 0;
    }
    factory.h
    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
     
    #ifndef FACTORY_H_INCLUDED
    #define FACTORY_H_INCLUDED
     
    #include "form.h"
     
    class Fabrique
    {
    public:
     
        Form* create(std::string quoi)
        {
            if (quoi == "cercle") return new Cercle();
            if (quoi == "carree") return new Carree();
            return NULL;
        }
    };
     
    #endif // FACTORY_H_INCLUDED
    form.h
    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
     
    #ifndef FORM_H_INCLUDED
    #define FORM_H_INCLUDED
     
    class Form
    {
        public:
            virtual void Dessiner() = 0;
    };
     
    class Cercle
        : public Form
    {
        public:
            Cercle(){};
            ~Cercle(){};
     
            inline virtual void Dessiner()
            {
                std::cout << "Je suis un cercle" << std::endl;
            }
    };
     
    class Carree
        : public Form
    {
        public:
            Carree(){};
            ~Carree(){};
     
            inline virtual void Dessiner()
            {
                std::cout << "Je suis un carree" << std::endl;
            }
    };
     
    #endif // FORM_H_INCLUDED
    2eme version :
    main.cpp
    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
     
    #include <iostream>
    #include "form.h"
     
    using namespace std;
     
    int main()
    {
        Form* f1 = new Cercle;
        f1->Dessiner();
     
        Form* f2 = new Carree;
        f2->Dessiner();
     
        delete f1, f2;
     
        return 0;
    }
    form.h
    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
     
    #ifndef FORM_H_INCLUDED
    #define FORM_H_INCLUDED
     
    class Form
    {
        public:
            virtual void Dessiner() = 0;
    };
     
    class Cercle
        : public Form
    {
        public:
            Cercle(){};
            ~Cercle(){};
     
            inline virtual void Dessiner()
            {
                std::cout << "Je suis un cercle" << std::endl;
            }
    };
     
    class Carree
        : public Form
    {
        public:
            Carree(){};
            ~Carree(){};
     
            inline virtual void Dessiner()
            {
                std::cout << "Je suis un carree" << std::endl;
            }
    };
     
    #endif // FORM_H_INCLUDED
    Pour moi, la 2eme version est plus facile d'utilisation pour un résultat identique.

    A mon avis, j'ai mal compris l'idée du pattern Factory :
    http://come-david.developpez.com/tut...e=Fabrique#LIV

    Pourtant, je pense l'avoir bien implémenté...

    Pouvez vous m'éclairez d'avantage ?

    Merci à tous
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  2. #2
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Imagine la situation suivante, à l'execution le programme demande à l'utilisateur ce qu'il veut créer sous la forme d'un identifiant (une chaine de caractère, ou autre, peu importe) parmi un ensemble de possibilité. La factory te permet de faire ca. Tu l'as mal implémenter, normalement il y a une fonction qui permet d'enregistrer les divers élément que tu veux créer. Relis le code de David Come, ou l'implémentation de ce pattern dans certaines bibliothèque : Poco, Loki.

  3. #3
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Le pattern factory a, justement, pour but de te permettre d'utiliser le polymorphisme sans avoir à t'inquiéter du type réel de l'objet créé, et donc, en minimisant les dépendances entre l'endroit où l'objet polymorphe est utilisé et le type réel de celui-ci.

    Dans le cas que tu présentes, l'idée est de permettre à l'utilisateur de connaitre l'existence de la fabrique d'une part et du type "Forme" d'autre part, sans s'inquiéter du fait que Forme est dérivé en plusieurs types particuliers.

    Après tout: tant que tu te contente d'invoquer des comportements polymorphes tels que dessiner sur tes objets, tu n'as strictement pas besoin de savoir qu'il existe différents type de Forme


    Cela te permet, non seulement, d'adopter un desing beaucoup plus simple, mais aussi, d'augmenter le respect du principe nommé OCP (Open Close Principle)
    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
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Imagine la situation suivante, à l'execution le programme demande à l'utilisateur ce qu'il veut créer sous la forme d'un identifiant (une chaine de caractère, ou autre, peu importe) parmi un ensemble de possibilité. La factory te permet de faire ca. Tu l'as mal implémenter, normalement il y a une fonction qui permet d'enregistrer les divers élément que tu veux créer. Relis le code de David Come, ou l'implémentation de ce pattern dans certaines bibliothèque : Poco, Loki.
    Dans un design Factory, il y a toujours une fonction Register ? Car j'ai (cru) voir des codes sans cette fonction et je n'en vois pas trop son utilité

    Donc si je comprends bien, avec ce pattern, je peux demander à l'utilisateur de dessiner un cercle (en récupérant sa saisie dans un cin par exemple) ou une autre forme parmi celle que j'ai programmées ? Alors qu'avec du polymorphisme classique, c'est impossible... AI-je bien compris ?

    Citation Envoyé par koala01
    Dans le cas que tu présentes, l'idée est de permettre à l'utilisateur de connaitre l'existence de la fabrique d'une part et du type "Forme" d'autre part, sans s'inquiéter du fait que Forme est dérivé en plusieurs types particuliers.

    Après tout: tant que tu te contente d'invoquer des comportements polymorphes tels que dessiner sur tes objets, tu n'as strictement pas besoin de savoir qu'il existe différents type de Forme
    Je n'arrive pas à saisir ce que vous voulez dire et surtout l'intérêt de ce pattern. Peut être que mon exemple est nul donc si vous aviez un autre exemple plus parlant sur le réel intérêt de ce pattern par rapport au polymorphisme classique, je suis preneur
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  5. #5
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Il n'y a pas de choix à faire entre ce pattern et le polymorphisme. Bien au contraire si tu utilises le pattern factory, tu utilises aussi le polymorphisme


    Le polymorphisme est un mécanisme qui permet d'avoir des comportements différent selon le type réel de l'objet et non le type statique (celui que "voit" le compilateur).

    La factory te permet de choisir que type d'objet créer de manière dynamique (ie à l'execution).


    Là où tu utilises le polymorphisme c'est que ta fonction de création de ta factory va te retourner via pointeur (ou référence) sur un type de base des objets de types filles : le type réel est celui du type fille que tu veux, le type statique est celui du type de base, dès que tu vas utiliser ces objets le polymorphisme entre en jeu, les bonnes fonction sont appelé.

    Pour la fonction register, la manière classique de faire est avec celle-ci (il faut que tu enregistres préallablement les différents objets (*) que tu veux pouvoir instancier ensuite). Pour des besoins précis on peut peut-être s'en passer, mais c'est du cas par cas


    Regardes ceci : http://pocoproject.org/slides/030-MemoryManagement.pdf (pages 31 à 38), c'est la factory proposé par Poco (*). Tu peux aussi regarder le code (il y a des élément multi-thread dedans, oublie les dans un premier temps). Je trouve l'interface assez simple et clair (Loki est plus complexe)



    (*) Pour être plus précis ce peut être des objets d'un type différent qui permet de créer des objets des types voulus.

  6. #6
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Le polymorphisme, c'est avant tout manipuler divers objets sans connaître leur type exact. Or il y a un moment où la connaissance du type exact est nécessaire : La création de l'objet.

    Le but du pattern factory est d'encapsuler la connaissance du type exact à la création, de manière à avoir un choix flexible de ce type.

    A titre d'exo, je t'invite à écrire du code pour sauver dans un fichier, puis surtout recharger de ce fichier, un vector<Form*>. L'intérêt de la factory devrait t'apparaître.

    La notion de register est très importante, mais j'avoue que je m'en passe assez souvent quand je dois code rapidement un prototype. Un des concepts de l'orienté objet est l'open-closed principle : On peut modifier le comportement d'un code en ajoutant du code, mais sans modifier le code existant. C'est à ça que sert register : Tu peux ajouter une nouvelle classe à celles gérées par la factory, sans changer une ligne de code dans le code existant, simplement en enregistrant ta nouvelle classe.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  7. #7
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Form* f1 = factory->create("cercle");
    Form* f2 = factory->create("carree");
    Imagine maintenant que tu as un programme en ligne de commande, et que c'est en cours d'exécution que l'utilisateur demande au programme de créer un cercle ou un carré.

  8. #8
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Le polymorphisme, c'est avant tout manipuler divers objets sans connaître leur type exact. Or il y a un moment où la connaissance du type exact est nécessaire : La création de l'objet.

    Le but du pattern factory est d'encapsuler la connaissance du type exact à la création, de manière à avoir un choix flexible de ce type.

    A titre d'exo, je t'invite à écrire du code pour sauver dans un fichier, puis surtout recharger de ce fichier, un vector<Form*>. L'intérêt de la factory devrait t'apparaître.

    La notion de register est très importante, mais j'avoue que je m'en passe assez souvent quand je dois code rapidement un prototype. Un des concepts de l'orienté objet est l'open-closed principle : On peut modifier le comportement d'un code en ajoutant du code, mais sans modifier le code existant. C'est à ça que sert register : Tu peux ajouter une nouvelle classe à celles gérées par la factory, sans changer une ligne de code dans le code existant, simplement en enregistrant ta nouvelle classe.
    Je crois que je commence à comprendre l'utilité de ce pattern. Avec ton exemple, sans la factory, il est impossible de charger des classes à partir d'un fichier texte

    Par contre, je suis entrain d'implémenter la version avec un register mais j'ai un problème car je ne veux pas créer les objets en les clonant mais en utilisant "new" :
    Citation Envoyé par tutoriel de come-david
    Avant toute chose, il faut préciser comment seront créés les objets. En effet, sur ce point, plusieurs stratégies sont envisageables telle la création via new ou celle par clonage. Pour ma part, j'ai choisi celle basée sur le clonage. Mais vous trouverez facilement d'autres implémentations sur le web.
    Je vous donne mon code :
    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
    class Fabrique
    {
    public:
     
        std::map<std::string, Form*> m_map;
     
        //Fonction qui associe clé <=> prototype
        void Register(const std::string& key, Form* obj)
        {
            //si la clé n'est pas déjà présente
            if(m_map.find(key)==m_map.end())
            {
                //on ajoute l'objet dans la map
                m_map[key] = obj;
            }
        }
     
     
        Form* create(const std::string& key)
        {
            Form* tmp = NULL;
            std::map<std::string, Form*>::const_iterator it = m_map.find(key);
     
            //si l'itérateur ne vaut pas map.end(), cela signifie que que la clé à été trouvée
            if(it != m_map.end())
            {
                //tmp = (*it).second->Clone();
                // ici comment dire si je veux un carree alors faire new Caree(); ...
            }
     
            return tmp;
        }
    };
    Dans tous les codes sur le net, ils utilisent des if pour faire un truc du genre :
    // Si key == "carree" alors return new Carree();
    // si key == "cercle" alors return new Cercle();
    Sauf que si j'ai bien compris et que je veux respecter l'open-closed en POO, je ne dois pas modifier le code de la classe si je décide d'enregistrer une nouvelle classe.
    Or avec la technique des "if" je dois modifier le code à chaque ajout... D'où l'utilisation d'une map je suppose mais encore une fois, je ne vois pas comment faire

    EDIT :
    J'ai vu que la fonction Register était statique. Est-ce obligatoire ? qu'est ce que ca change concrètement ?
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  9. #9
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    As-tu regarder l'interface proposé par Poco ? C'est vraiment instructif.

    Voila en gros à quoi ca ressemble (code partiel) :
    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
     
    #include <map>
    #include <string>
     
    template<class Base>
    struct Instanciator
    {
      virtual Base* create() =0;
      virtual ~Instanciator() {}
    };
     
    template<class Base, class Fille>
    struct NewInstanciator : Instanciator<Base>
    {
      Base* create()
      { return new Fille(); }
    };
     
    template<class Base>
    class Factory
    {
      std::map<std::string,Instanciator<Base>*> map_;
      public:
        void register_class(const std::string& id,Instanciator<Base>* inst)
        { map_[id] = inst; /*gérer les cas où l'id est déjà enregistrer*/ }
        template<class Fille>
        void register_class(const std::string& id)
        { register_class(id,new NewInstanciator<Base,Fille>()); }
        Base* create(const std::string& id)
        { return map_.at(id).create(); }
        ~Factory()
        { /*détruire les instanciators*/ }
    };
     
    template<class Base,class Fille>
    class CloneInstanciator : public Instanciator<Base>
    {
      Fille* to_clone;
      public:
        CloneInstanciator(Fille* f) : to_clone(f) {}
        Base* create()
        { return to_clone.clone(); }
    };
    Cette facon de faire permet de laisse le choix de la politique de création (clone ou new) hors de la classe. On enregistre des instanciator (via des pointeurs) qui héritent d'un type commun (Instanciator<Base>) et possède une méthode de création (Base* create()).

    On peut aussi faire une seconde fonction d'enregistrement template qui permet d'enregistrer un instanciator en connaisant le type réel.

  10. #10
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Perso, j’utilise généralement un map<string, function<Base*()> >
    Avec function == boost::function, ou std::function pour un compilateur récent, et éventuellement des paramètres supplémentaires si toutes les classes de ma hiérarchie ont besoin de tels paramètres.

    Mais c'est pas forcément hyper facile à comprendre. Une map contenant des pointeurs de fonction est moralement équivalent (mais moins souple):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    typedef Base* (*Creator)();
    map<string, Creator> maMap;
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  11. #11
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Je vous avoue que je suis perdu
    Les templates c'est pas trop mon truc même si je sais les utiliser. Mais cette histoire de classe Instanciator m'a perdu complètement.

    Concrètement faire la création des objets par un new ou par une méthode de clonage ca change quoi ?

    EDIT :
    Dans son tutoriel, il dit qu'il fait la création par clonage masi pourquoi ca procédure de clonage ne fait qu'un new ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Figure* Cercle::Clone() const 
    { 
            return new Cercle(*this);
    }
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  12. #12
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Pour simplifier ce qui a été dit, on utilise le pattern factory pour créer des objets dont la classe n'est pas connue au moment de la compilation - le choix s'effectuant donc au runtime. Par rapport à une approche basée sur une série de if, le pattern factory permet un meilleur respect d'un principe d'architecture objet nommé "principe ouvert/fermé".

    En gros, il est plus facile et plus maintenable de faire :

    objbuilder* obj_builder = new my_new_object_builder();
    factory,register(obj_builder);
    ...
    object* o = factory.create(some_particular_id);
    Que de modifier un gros if avec des dizaines de cas.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  13. #13
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Pour simplifier ce qui a été dit, on utilise le pattern factory pour créer des objets dont la classe n'est pas connue au moment de la compilation - le choix s'effectuant donc au runtime. Par rapport à une approche basée sur une série de if, le pattern factory permet un meilleur respect d'un principe d'architecture objet nommé "principe ouvert/fermé".

    En gros, il est plus facile et plus maintenable de faire :



    Que de modifier un gros if avec des dizaines de cas.
    Ca il n'y a pas de problème, j'ai compris l'intérêt de la fonction Register

    Mais j'aimerais bien comprendre les codes de JolyLoic et
    Flob90 car ils ont l'air intéressant est très flexibles.
    Et je n'aime pas recopier du code sans comprendre ce qu'il fait c'est pour ca que je m'acharne à essayer de comprendre des principes pas évidents

    Est ce que vous pourriez (Flob90, JolyLoic ou quelqu'un d'autre ) me donner la version sans template et appliqué à mes classes (à savoir Form, Carree, Cercle et Factory) histoire que je comprenne le principe ? Après je m'occuperais de templatisé tout ca quand j'aurais bien compris le fonctionnement

    Merci !
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  14. #14
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Aspic Voir le message
    EDIT :
    Dans son tutoriel, il dit qu'il fait la création par clonage masi pourquoi ca procédure de clonage ne fait qu'un new ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Figure* Cercle::Clone() const 
    { 
            return new Cercle(*this);
    }
    http://cpp.developpez.com/faq/cpp/?p...es#CLASS_clone devrait te donner une meilleure compréhension.
    Citation Envoyé par Aspic Voir le message
    Est ce que vous pourriez (Flob90, JolyLoic ou quelqu'un d'autre ) me donner la version sans template et appliqué à mes classes (à savoir Form, Carree, Cercle et Factory) histoire que je comprenne le principe ? Après je m'occuperais de templatisé tout ca quand j'aurais bien compris le fonctionnement
    L'exemple que j'ai donné avec Creator est sans template (mais avec pointeur de fonction).
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  15. #15
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Ok j'ai compris pour la pattern Clone (et hop un nouveau pattern découvert ^^)

    Ok je crois que j'ai réussi à implémenter la version avec template. Pouvez vous vérfier que j'ai fais de bêtises ?
    factory.h
    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
    68
    69
    70
    71
    72
    73
    74
    75
    #ifndef FACTORY_H_INCLUDED
    #define FACTORY_H_INCLUDED
     
    #include <map>
    #include <string>
     
    template<class Base>
    struct Instanciator
    {
        virtual Base* create() =0;
        virtual ~Instanciator() {}
    };
     
    template<class Base, class Fille>
    struct NewInstanciator
        : public Instanciator<Base>
    {
        Base* create()
        {
            return new Fille();
        }
    };
     
    template<class Base, class Fille>
    class CloneInstanciator
        : public Instanciator<Base>
    {
        Fille* to_clone;
    public:
        CloneInstanciator(Fille* f) : to_clone(f) {}
        Base* create()
        {
            return to_clone->Clone();
        }
    };
     
    template <class Base>
    class Factory2
    {
        std::map<std::string,Instanciator<Base>*> map_;
    public:
        void register_class(const std::string& id, Instanciator<Base>* inst)
        {
            /*gérer les cas où l'id est déjà enregistrer*/
            //si la clé n'est pas déjà présente
            if(map_.find(id)==map_.end())
            {
                //on ajoute l'objet dans la map
                map_[id] = inst;
            }
        }
     
        template<class Fille>
        void register_class(const std::string& id)
        {
            register_class(id,new NewInstanciator<Base, Fille>());
        }
     
        Base* create(const std::string& id)
        {
            return map_.at(id)->create();
        }
        ~Factory2()
        {
            /*détruire les instanciators*/
            typename std::map<std::string,Instanciator<Base>*>::iterator it;
     
            for(it=map_.begin() ; it!=map_.end() ; ++it)
            {
                delete it->second; // accede à la valeur donc à Instanciator*
            }
        }
    };
     
    #endif // FACTORY_H_INCLUDED
    Form.h et Form.cpp
    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
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    #ifndef FORM_H_INCLUDED
    #define FORM_H_INCLUDED
     
    //la classe prototype abordé en II
    template <class T>
    class Prototype
    {
        public:
        virtual ~Prototype(){}
        virtual T* Clone() const = 0;
    };
     
    class Form
        : public Prototype<Form>
    {
        public:
            virtual void Dessiner() = 0;
    };
     
    class Cercle
        : public Form
    {
        public:
            Cercle();
            ~Cercle();
     
            void Dessiner();
            Form* Clone() const;
    };
     
    class Carree
        : public Form
    {
        public:
            Carree();
            ~Carree();
     
            void Dessiner();
            Form* Clone() const;
    };
     
    class Triangle
        : public Form
    {
        public:
            Triangle();
            ~Triangle();
     
            void Dessiner();
            Form* Clone() const;
    };
     
    #endif // FORM_H_INCLUDED
    #include "form.h"
    #include <iostream>
     
    Cercle::Cercle(){}
    Cercle::~Cercle(){}
     
    void Cercle::Dessiner()
    {
        std::cout << "Je suis un cercle" << std::endl;
    }
     
    Form* Cercle::Clone() const
    {
        return new Cercle(*this);
    }
     
    Carree::Carree(){}
    Carree::~Carree(){}
     
    void Carree::Dessiner()
    {
        std::cout << "Je suis un carree" << std::endl;
    }
     
    Form* Carree::Clone() const
    {
        return new Carree(*this);
    }
     
     
    Triangle::Triangle(){}
    Triangle::~Triangle(){}
     
    void Triangle::Dessiner()
    {
        std::cout << "Je suis un Triangle" << std::endl;
    }
     
    Form* Triangle::Clone() const
    {
        return new Triangle(*this);
    }
    Et enfin le main.cpp
    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
    #include <iostream>
    #include "factory.h"
    #include "form.h"
     
    using namespace std;
     
    int main()
    {
        Factory2<Form>* fac = new Factory2<Form>();
     
        fac->register_class<Carree>("carre");
        //equivalent à fac->register_class("carre", new NewInstanciator<Form, Carree>());
     
        fac->register_class("cercle", new CloneInstanciator<Form, Cercle>(new Cercle));
     
        Form* f1 = fac->create("carre");
        f1->Dessiner();
        Form* f2 = fac->create("cercle");
        f2->Dessiner();
     
        delete f1;
        delete f2;
        delete fac;
     
        return 0;
    }
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  16. #16
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Oui c'est ca, dans ton exemple, les carré sont créer avec un politique de type "new" et les cercle une politique par clonage.

    Un oublie dans le code que j'avais posté (et donc dans le tiens) dans CloneInstanciator il faut plutôt utiliser un std::unique_ptr<Fille> pour to_clone, sinon il y a une fuite mémoire. (ca implique que le CloneInstanciator est responsable de la durée de vie de l'objet à cloner).

    Ce que propose par JolyLoic est similaire, on remplace juste les instanciators par des std::function<Base*()> (ou autre type de functor) et à la place d'appeler la fonction create tu appeles le foncteur. Le principe est le même : tu as un objet qui te créé ce que tu lui demande. C'est à cette objet de choisir comment le créer : new, clone, autre.

    NB: Les templates servent juste à pas avoir à écrire les instanciator pour chaque type : le code de NewInstanciator ou CloneInstanciator est à chaque fois le même. Tu peux les enlèver mais tu vas avoir un duplication de code assez grosse, AMA

  17. #17
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Parfait, merci ! justement j'allais testé les fuites de mémoire merci pour l'info ^^

    Encore une dernière précision, j'ai vu que la méthode Register était statique dans le tuto de come-david, pourquoi cela ?

    Sinon à titre d'application de ce pattern, est ce que dans la situation présente, l'utilisation de la Fabrique est justifiée ou pas :
    Soit une classe Object qui gère des objets de tout type comme des coeurs, potions, rochers, blocs... sachant que chaque type d'objet possède des caractéristiques précises. Exemple : les rochers peuvent se briser mais pas se pousser alors qu'un bloc pour se pousser (et même on peut le porter...). Les coeurs et les potions sont "collectables" comme dans 90% des jeux.
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  18. #18
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Parfait, merci ! justement j'allais testé les fuites de mémoire merci pour l'info ^^

    Encore une dernière précision, j'ai vu que la méthode Register était statique dans le tuto de come-david, pourquoi cela ?

    Sinon à titre d'application de ce pattern, est ce que dans la situation présente, l'utilisation de la Fabrique est justifiée ou pas :
    Soit une classe Object qui gère des objets de tout type comme des coeurs, potions, rochers, blocs... sachant que chaque type d'objet possède des caractéristiques précises. Exemple : les rochers peuvent se briser mais pas se pousser alors qu'un bloc pour se pousser (et même on peut le porter...). Les coeurs et les potions sont "collectables" comme dans 90% des jeux.
    J'aurais tendance à dire qu'il faut faire attention de ne pas tomber dans le piège du "god object"...

    Il serait peut etre plus intéressant de regrouper les types selon leur utilité (objets "utilisable" d'un coté, éléments décors de l'autre, armes d'un troisième, armures d'un quatrième, etc)

    et d'avoir, au final, autant de fabrique et de gestionnaires que... d'utilités envisagées.

    Cela simplifiera sans doute énormément la gestion au quotidien de tous tes objets, même si on peut imaginer qu'ils présentent malgré tout une interface commune ( void use() ) ne serait-ce que parce que tu ne devras plus tester systématiquement si tu as affaire à une potion, à un bijou ou à une arme

    Par contre, un petit coup de template t'aidera surement à éviter la duplication de code
    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

  19. #19
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Citation Envoyé par koala01 Voir le message
    J'aurais tendance à dire qu'il faut faire attention de ne pas tomber dans le piège du "god object"...

    Il serait peut etre plus intéressant de regrouper les types selon leur utilité (objets "utilisable" d'un coté, éléments décors de l'autre, armes d'un troisième, armures d'un quatrième, etc)

    et d'avoir, au final, autant de fabrique et de gestionnaires que... d'utilités envisagées.

    Cela simplifiera sans doute énormément la gestion au quotidien de tous tes objets, même si on peut imaginer qu'ils présentent malgré tout une interface commune ( void use() ) ne serait-ce que parce que tu ne devras plus tester systématiquement si tu as affaire à une potion, à un bijou ou à une arme

    Par contre, un petit coup de template t'aidera surement à éviter la duplication de code
    Donc si je comprends bien, je vais avoir une classe Mère ObjetUtilisable avec des classes Filles Coeur, Potion...
    Une autre classe Mère ObjetDecors avec comme classes Filles Rocher, Herbe, Bloc, Mur...

    Mais où sont les Fabriques dans ce cas ?
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  20. #20
    screetch
    Invité(e)
    Par défaut
    c'est une (très) mauvaise idée d'avoir des classes our chaque type d'objet.

    Un moyen plus flexibe et bien meilleur au lieu de l'héritage, c'est la composition; un Objet est composé d'aspects, un aspect est par exemple son aspect a l'ecran (son sprite)
    un autre aspect c'est son aspect "poussable"
    un autre aspect c'est "cassable", ou bien "ramassable"

    ca permet de créer des entités qui sont a la fois Utilisable et Ramassable, sans avoir de l'heritage multiple ou pire, de l'heritage foireux.

    L'heritage de toutes facons c'est le mal.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 16
    Dernier message: 26/08/2011, 22h02
  2. [MySQL] Demande précisions sur LOCK TABLE
    Par renaud26 dans le forum PHP & Base de données
    Réponses: 8
    Dernier message: 15/04/2011, 13h49
  3. Demande de précision sur "Extends" ..
    Par Invité dans le forum Langage
    Réponses: 6
    Dernier message: 12/02/2006, 14h25
  4. Demande de précisions sur Backup/Restore et transactions
    Par lio33 dans le forum Connexion aux bases de données
    Réponses: 1
    Dernier message: 16/11/2005, 12h08
  5. [Observateur] Précisions sur le design pattern Observer [UML]
    Par joquetino dans le forum Design Patterns
    Réponses: 2
    Dernier message: 07/10/2004, 22h35

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