Question de design et de généricité
Bonjour,
Bon, désolé pour le titre, je n'ai pas trouvé comme résumer moins vaguement ce qui m'amène !
Je cherche une bonne façon de programmer ceci :
- J'ai une collection d'objets instances d'une même classe A
- Je veux donner à l'utilisateur du programme une interface graphique (GUI) qui lui permette de modifier les attributs d'un de ces objets au choix
- Il ne doit y avoir qu'une GUI affichée en même temps
Je vois plusieurs possibilités :
Approche 1 - La classe A implémente la GUI, qu'on peut activer ou désactiver
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class A {
public:
void setGuiActive( bool active );
void displayGui(); // Affiche le GUI seulement s'il est activé
void foo();
};
vector< A > objects( 10 ); // vector pour l'exemple, mais d'autre conteneurs pourraient être utilisés
objects.back().setGuiActive( true );
void mainLoop(){
for( A & a : objects ){
a.foo();
a.displayGui();
}
} |
Je n'ai pas rendu visible dans cet extrait de code le fait que la classe A a des variables membres, que la GUI permet de modifier les valeurs de certaines de ces variables, et que le résultat de foo() est fonction de ces valeurs.
Deux inconvénients avec cette approche :
- Il faut implémenter un mécanisme qui assure qu'une seule GUI est active
- Si la GUI utilise de nombreuses variables membre, la classe A est alourdie pour rien
Approche 2 - La classe A implémente la GUI, et une variable référence l'objet pour lequel on veut afficher la GUI
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class A {
public:
void displayGui();
void foo();
};
vector< A > objects( 10 );
A * selected = &objects.back();
void mainLoop(){
for( A & a : objects ) a.foo();
if( selected ) selected->displayGui();
} |
Ceci résout le premier inconvénient de l'approche 1, mais pas le 2eme:
- Si la GUI utilise de nombreuses variables membre, la classe A est alourdie pour rien
Approche 3 - La GUI est implémentée dans une autre classe
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class A {
public:
void foo();
};
class Gui {
public:
void setTarget( A * target);
void display(); // N'affiche rien si target n'est pas définie
private:
A * target = nullptr;
};
vector< A > objects( 10 );
Gui gui;
gui.setTarget( &objects.back() );
void mainLoop(){
for( A & a : objects ) a.foo();
gui.display();
} |
Ceci résout les 2 inconvénients de l'approche 1, mais en introduisant l'inconvénient que Gui référence un object A
Cette 3e approche me semble préférable car elle ne surcharge pas la classe A, et je pars donc sur cette option. Mais peut-être aurez-vous déjà des suggestions quand à ce choix, ou une autre approche à proposer ?
Etant parti sur cette 3e approche, je cherche a bien gérer le fait que Gui référence un objet A.
Dans l'exemple que j'ai donné, c'est à l'utilisateur de Gui de faire attention à l'utilisation de pointeur invalide:
Code:
1 2 3 4 5
| vector< A > objects( 10 );
Gui gui;
gui.setTarget( &objects.back() );
objects.clear();
gui.display(); // Aïe ! Le target de gui n'est plus valide |
Il serait possible d'utiliser un weak_ptr pour prévenir ce problème:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Gui {
public:
void setTarget( weak_ptr<A> target );
void display(); // N'affiche rien si target.lock() n'est plus valide
private:
weak_ptr<A> target;
};
vector< share_ptr<A> > objects;
for( int i = 0; i < 10; ++i ) objects.push_back( make_shared<A>() );
Gui gui;
gui.setTarget( &objects.back() );
objects.clear();
gui.display(); // Ouf, l'utilisation et le test du weak_ptr prévient le bug |
Je parle d'utilisateur de la classe Gui car c'est un code que je vais partager, et je voudrais donc concevoir un combo A + Gui qui soit assez générique pour d'adapter aux différents besoins des utilisateurs de ses classes, que ce soit d'autres personnes ou moi-même dans 2 ans.
Comme cela me gène d'imposer à l'utilisateur de Gui l'usage de shared_ptr pour créer ses objects A, j'en suis venu à envisager la création d'une classe Gui qui puisse au choix utiliser un pointeur nu ou un weak_ptr. Pour faire cela j'ai posé cette question sur le forum. Et c'est parce-que koala01 y écrit que mon design lui semble douteux que j'en viens à vous demander vos avis dans ce présent sujet.
Que pensez-vous de mon choix de l'approche n°3 ?
Que pensez-vous de vouloir offrir à l'utilisateur le choix de sa stratégie de construction et destruction des objets A ?
Merci !