std::sharedptr<Forme> Instanciate(int type)
{
switch(type)
{
case 0:
return new Cercle;
case 1:
return new Line;
case 2:
return new Ellipse;
default:
break;
}
return NULL;
}
En réalité, j'ai un polymorphisme avec beaucoup plus de cas, et donc un switch qui grossit, grossit !
N'y aurait il pas une possibilité avec du template de faire un truc beaucoup plus compact ?
J'aimerais écrire quelque part un truc genre {0,Cercle},{1,Line},{2,Ellipse} etc... et qu'ensuite la fonction prenne 4 - 5 lignes.
Moi généralement j'utilise un truc assez proche de ce que faisait Alexandrescu dans son Modern C++ Design (donc ça doit être dans Loki en plus propre je suppose)
Avec ce fonctionnement, c'est chaque classe qui s'enregistre elle-même auprès de la factory, comme ça pas de soucis d'OCP comme dans ton cas. Ca peut même se factoriser par CRTP comme je le fais dans le projet en lien.
Un truc dans ce genre (avec un identification plus performante) ?
//mother.hh
#ifndef _MOTHER
#define _MOTHER
#include <functional>
#include <string>
#include <map>
class Mother;
namespace std {
template <typename T>
using add_pointer_t = typename add_pointer<T>::type;
}
using MotherCreator = std::add_pointer_t<Mother*()>;
class Mother{
public :
virtual ~Mother(){}
virtual void print() const;
static Mother* create(std::string const& name);
static void registerToFactory(std::string const& value, MotherCreator func);
protected:
//constructeur protégé = seul les amis/enfants peuvent me construire
Mother(){}
private :
//permet d'éviter un "fiasco sur l'initialisation" voir FaQ developpez.com
static std::map<std::string, MotherCreator>& factories();
};
#endif
//mother.cpp
#include <iostream>
#include "mother.hh"
//permet d'éviter un "fiasco sur l'initialisation" voir FaQ developpez.com
std::map<std::string, MotherCreator>& Mother::factories(){
static std::map<std::string, MotherCreator> real_factories;
return real_factories;
}
void Mother::print() const{
std::cout<<"Je suis Mother"<<std::endl;
}
Mother* Mother::create(std::string const& name){
//on cherche la factory nommé "name"
auto it(factories().find(name));
//on renvoie la construction voulue
if(it != factories().end()) return it->second();
else throw "not in factories";
}
//permet à une factory de s'enregistrer auprès de Mother
void Mother::registerToFactory(std::string const& value, MotherCreator func){
factories()[value] = func;
}
/*
NOTE :
Il est important de noter que Mother NE CONNAIT AUCUN ENFANT.
Mieux encore : ELLE N'A PAS BESOIN DE LES CONNAITRE
Résultat : on peut ajouter des enfants sans que cela nécessite d'inclusion
*/
//child.hh
#ifndef _CHILD
#define _CHILD
#include "mother.hh"
class Child : public Mother{
public :
void print() const override;
private:
Child(){} //on ne peut être construit que par nos amis
class Factory; //on cache la Factory
friend class Child::Factory; //en l'occurrence, notre factory
static Child::Factory* factory; //qui est statique et privée
};
#endif
//child.cpp
#include <iostream>
#include "child.hh"
//Notre factory cachée
class Child::Factory{
public :
Factory(){
//à la construction qui n'est appelée que dans ce fichier
//on s'enregistre dans la factory avec un identifiant
Mother::registerToFactory("Child", create);
}
static Mother* create();
};
//on crée la factory
Child::Factory* Child::factory = new Child::Factory();
//qui renvoie des enfants
Mother* Child::Factory::create(){
return new Child();
}
void Child::print() const{
std::cout<<"Je suis Child"<<std::endl;
}
//otherchild.hh
#ifndef _OTHERCHILD
#define _OTHERCHILD
#include "mother.hh"
class OtherChild : public Mother{
public :
void print() const override;
private:
OtherChild(){} //on ne peut être construit que par nos amis
class Factory; //on cache la Factory
friend class OtherChild::Factory; //en l'occurrence, notre factory
static OtherChild::Factory* factory;
};
#endif
//otherchild.cpp
#include <iostream>
#include "other_child.hh"
//Notre factory cachée
class OtherChild::Factory{
public :
Factory(){
//à la construction qui n'est appelée que dans ce fichier
//on s'enregistre dans la factory avec un identifiant
Mother::registerToFactory("OtherChild", create);
}
static Mother* create();
};
//on crée la factory
OtherChild::Factory* OtherChild::factory = new OtherChild::Factory();
//qui renvoie des enfants
Mother* OtherChild::Factory::create(){
return new OtherChild();
}
void OtherChild::print() const{
std::cout<<"Je suis OtherChild"<<std::endl;
}
//main.cpp :
#include <memory>
#include "mother.hh"
int main(){
//NOTE ULTRA-IMPORTANTE :
// Même ici on a pas besoin de connaître les enfants et leur type !
// On les crée par leur identifiant !
// Polymorphisme POWA : On peut ajouter des enfants après avoir codé tout ça
// les utiliser dans un algo aléatoire : on ne connaît pas les types réels
// donc ça marche !
std::unique_ptr<Mother> ptr_c( Mother::create("Child") );
std::unique_ptr<Mother> ptr_oc( Mother::create("OtherChild") );
ptr_c->print();
ptr_oc->print();
return 0;
}
Justement, ce que je reproche à cette solution est au final la même que le switch, problème d'OCP.
Quand on rajoute une classe, il faut pas oublier d'aller aussi modifier le tableau, et c'est vite arrivé^^
Alors qu'avec une factory (bien entendu, pas forcément besoin de template, on peut faire simple uniquement pour cette classe, les templates sont juste là pour ne pas avoir à réécrire la même chose pour chaque hierarchie de classe), c'est que les classe s'enregistrent elles-même au niveau de la factory, comme ça y a pas de chance d'oublier (où alors si ce qui faut pour n'est pas fait, ça compile pas).
Bon après tout n'est pas blanc non plus, le soucis des factory comme ça si elles fournissent elles-même leur clé, c'est qu'il faut s'assurer que cette clé est unique...
Justement, ce que je reproche à cette solution est au final la même que le switch, problème d'OCP.
Quand on rajoute une classe, il faut pas oublier d'aller aussi modifier le tableau, et c'est vite arrivé^^
Alors qu'avec une factory (bien entendu, pas forcément besoin de template, on peut faire simple uniquement pour cette classe, les templates sont juste là pour ne pas avoir à réécrire la même chose pour chaque hierarchie de classe), c'est que les classe s'enregistrent elles-même au niveau de la factory, comme ça y a pas de chance d'oublier (où alors si ce qui faut pour n'est pas fait, ça compile pas).
Bon après tout n'est pas blanc non plus, le soucis des factory comme ça si elles fournissent elles-même leur clé, c'est qu'il faut s'assurer que cette clé est unique...
- Edité par epso il y a 16 minutes
Je voulais juste montrer la base du mécanisme. Tu remplace le tableau figé par une map identifiant->fonction, c'est le même truc.
Après je dois avoir quelque part un truc encore plus mieux, qui est le chargement dynamique (dlopen pour linux) des classes (plugins) présentes dans un répertoire, qui s'enregistrent toutes seules.
- Edité par michelbillaud 16 décembre 2014 à 12:19:13
Le tutoriel de Dvp date un peu, mais il reste intéressant a lire : http://come-david.developpez.com/tutoriels/dps/. Modern C++ Design et le GoF sont intéressant a lire aussi (forcement). On trouve aussi pas mal de tutos sur internet (sur SO en particulier) pour des implémentations plus "moderne" des DP (par exemple utiliser un variadic template pour create, pour construire des objets en utilisant une liste d'arguments différentes selon le type d'objet).
Il est également possible de faire des versions compile-time de factory, mais j'aime bien la version runtime + plugin (voir par exemple le code de Qt Creator - et plus généralement de Qt, il utilises pas mal cette approche).
@Ksass`Peuk: il est quand même préférable de séparer la factory de la classe Mother. Et d’éviter la static map
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Discord NaN. Mon site.