OVH Cloud OVH Cloud

Utilité des classes singleton ?

4 réponses
Avatar
Aurélien REGAT-BARREL
Bonjour,
Je développe une appli graphique, et j'ai une classe singleton Model qui
contient toute la partie "métier" de mon appli, le modèle quoi. J'ai plein
de vues qui utilisent cette classe modèle, un peu trop à mon goût. A vrai
dire c'est plus ou moins une variable globale la classe singleton, et ça me
gêne un peu d'avior plein de références peu visibles vers cette classe
pillier de mon appli. En effet l'include "model.h" se fait dans le .cpp et
donc il n'est pas possible en regardant dans le .h des classes de savoir si
elle touche au modèle ou pas. Ca me dérange, je crains des effets de bords
désagréables à terme.

Je compte donc supprimer/modifier cette classe singleton par une classe
"utilisateur" que devront hériter toutes les vues.
Avant j'avais :

// vue 1 : modifie le modele
class Fenetre1 : public FenetreDeBase
{
public:
void buttonSaveClicked()
{
Model::GetInstance().Save(); // modifie !
}
};

// vue 2 : ne modifie pas le modele
class Fenetre2 : public FenetreDeBase
{
public:
void buttonOpenClicked()
{
this->UpdateView( Model::GetInstance() ); // lecture seule, const
Model & serait mieux
}
};

Je pense faire un truc du genre

// nouvelle classe singleton
// accès en lecture / écriture
class ModelView
{
public:
Model & GetInstance()
{
return this->_model;
}

private:
static Model _model; // instance unique
};

// accès en lecture seule
// héritage protégé : pas touche à l'ancien GetInstance non constant
class ModelConstView : protected ModelView
{
public:
const Model & GetInstance()
{
return ModelView::GetInstance();
}
};

Et mes classes fenêtres qui utilisent la vue doivent faire un héritage
(multiple) de ma nouvelle classe singleton qui n'en n'est plus vraiment une
:

class Fenetre1 : public ModelView, public FenetreDeBase
{
void buttonSaveClicked()
{
this->GetInstance().Save();
}
};

// vue 2
class Fenetre2 : public ModelConstView, public FenetreDeBase
void buttonOpenClicked()
{
this->UpdateView( this->GetInstance() );
}
};

Ainsi en un coup d'oeil dans la hiérarchie des classes je sais qui fait
quoi, qui touche ou non au modèle.
Qu'en pensez-vous ?

--
Aurélien REGAT-BARREL

4 réponses

Avatar
Jean-Marc Bourguet
"Aurélien REGAT-BARREL" writes:

Bonjour,
Je développe une appli graphique, et j'ai une classe singleton Model qui
contient toute la partie "métier" de mon appli, le modèle quoi. J'ai plein
de vues qui utilisent cette classe modèle, un peu trop à mon goût. A vrai
dire c'est plus ou moins une variable globale la classe singleton, et ça me
gêne un peu d'avior plein de références peu visibles vers cette classe
pillier de mon appli.


J'aurais simplement un pointeur vers le modele dans les classes
view/controller. Meme si pour le moment dans l'appli le modele est
unique, ca peut changer dans le futur et l'unicite de celui-ci ne me
semble qu'une propriete artificielle. Dans ce cas, un singleton n'est
pas une bonne solution.

Je pense faire un truc du genre

// nouvelle classe singleton
// accès en lecture / écriture
class ModelView
{
public:
Model & GetInstance()
{
return this->_model;
}

private:
static Model _model; // instance unique
};


J'aime pas le nom GetInstance pour ca, GetModel me semble meilleur.

Qu'en pensez-vous ?


Avec ma proposition, tu n'auras qu'a regarder les parametres des
constructeurs. Et je trouve le resultat meilleur. Je ne suis pas
contre les singletons, mais ici ca me semble de l'abus.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Aurélien REGAT-BARREL
J'aurais simplement un pointeur vers le modele dans les classes
view/controller. Meme si pour le moment dans l'appli le modele est
unique, ca peut changer dans le futur et l'unicite de celui-ci ne me
semble qu'une propriete artificielle. Dans ce cas, un singleton n'est
pas une bonne solution.


J'y avais pensé au pointeur global, mais j'ai opté pour une référence et
donc un singleton pour l'initialiser. Mais fondamentalement un pointeur
global ou un singleton c'est kifkif. C'est un accès direct.

J'aime pas le nom GetInstance pour ca, GetModel me semble meilleur.


Bingo, c'est ce que j'ai dans mon code ;) C'était pour l'exemple.

Avec ma proposition, tu n'auras qu'a regarder les parametres des
constructeurs. Et je trouve le resultat meilleur. Je ne suis pas
contre les singletons, mais ici ca me semble de l'abus.


Je vois pas le rapport avec les constructeurs. A moins que tu veuilles que
je passe le modele dans les constructeurs. Jusqu'à y'a pas longtemps c'est
ce que je faisais (sauf que c'était une référence encore une fois). Mais je
trouvais ça gavant de devoir trimballer partout ce pointeur, en plus des
déclarations alourdies des nouvelles classes. Grosso modo ma classe
ModelView est simplement une factorisation de ce code :

class Fenetre1
{
public:
Fenetre1( Model * M ) : _m( M ) {}

private:
Model * _m;
}:

class Fenetre2
{
public:
Fenetre2( Model * M ) : _m( M ) {}

private:
Model * _m;
}:
code répété dans toutes les classes fenêtres. Sauf que mainteant le pointeur
n'a plus à être passé, il est récupéré automatiquement.

class ModelView
{
public:
Model * GetModel() { return this->_m; }

private:
static Model * _m;
};

class Fenetre1 : public ModelView
{
// c'est fini
}:

class Fenetre2 : public ModelView
{
// c'est fini
}:

--
Aurélien REGAT-BARREL

Avatar
Jean-Marc Bourguet
"Aurélien REGAT-BARREL" writes:

Je vois pas le rapport avec les constructeurs. A moins que tu
veuilles que je passe le modele dans les constructeurs.


Gagne. C'est ce qui me semble le plus logique.

Jusqu'à y'a pas longtemps c'est ce que je faisais (sauf que c'était
une référence encore une fois).


Il me semble que j'ai mis pointeur ou reference, peut-etre que j'ai
virer en reredigeant.

Mais je trouvais ça gavant de devoir trimballer partout ce pointeur,
en plus des déclarations alourdies des nouvelles classes.


Si tu as tant de nouvelles vues que ca, c'est vraissemblable qu'elles
ne sont que des vues que d'une partie du modele. Il est possible que
tu as une classe modele qui fait trop de choses.

Je ne trouve pas particulierement bon que le modele dans une structure
MVC (meme si V et C sont fusionnes) soit un singleton, quelque soit la
maniere dont tu y accedes. Il me semble que la partie graphique (V et
C) ne doit pas savoir que le modele est unique ou non.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Aurélien REGAT-BARREL
Si tu as tant de nouvelles vues que ca, c'est vraissemblable qu'elles
ne sont que des vues que d'une partie du modele. Il est possible que
tu as une classe modele qui fait trop de choses.


C'est mon objet Model racine, qui contient plein d'autres petits modèles.
Jusque là chaque vue était dpécialisée sur un petit modèle, mais now j'ai un
singleton dehors et les vues se chargent d'aller y taper ce qu'elles
veulent. C'est pour ça que je suis assuré d'avoir toujours un seul modèle,
car en quelque sorte c'est un conteneur de modèles ma classe Model.

Je ne trouve pas particulierement bon que le modele dans une structure
MVC (meme si V et C sont fusionnes) soit un singleton, quelque soit la
maniere dont tu y accedes. Il me semble que la partie graphique (V et
C) ne doit pas savoir que le modele est unique ou non.


Je crois qu'il y a mélange dans nos échanges entre objet et instance
d'objet. C'est normal qu'une vue opère sur un unique modèle, et si j'ai bien
compris tu me met en garde qu'une vue de doit pas opérer sur une seule
instance d'un modèle. Moui c'est vrai j'ai sous-estimé ce point. Surtout que
pour certains mini-modèles il est justifié dans mon cas.
Faut que je réfléchisse. Est-ce que je passe une instance de mini-modèle à
traiter, ou le mega-modele avec le numéro de l'instance de mini-modèle à
traiter... hum... je crois que j'ai trouvé :-)
Ma solution de classe de base me plaisait bien quand même, histoire de
factoriser du code, et sur le diagramme UML ça ressort mieux qui fait quoi,
j'identifie bien le rôle de chaque classe (au lieu de "cette fenêtre édite
tel modèle", c'est "cette fenêtre est un éditeur de ce modèle", ça me plaît
bien).
La variante c'est que ma classe de base demandra le pointeur dans son
constructeur au lieu de gérer un singleton.
Faut que je repasse par une petite analyse entre mon méga modèle et mes
mini-modèles.
Merci.

--
Aurélien REGAT-BARREL