J'ai créé deux classes abstraites Subject et Observer qui définissent une interface de modèle Observer classique. Je dérive d'eux pour implémenter le modèle Observer. Un observateur pourrait ressembler à ceci:
void MyClass::Update(Subject *subject)
{
if(subject == myService_)
{
DoSomething();
}
else if(subject == myOtherService_)
{
DoSomethingElse();
}
}
C'est bien et ça me dit qui a changé quelque chose. Cependant, cela ne me dit pas ce qui a changé. Parfois, c'est ok parce que je vais juste interroger le sujet pour les dernières données, mais d'autres fois, j'ai besoin de savoir ce qui a changé exactement sur le sujet. Je remarque qu'en Java, ils ont à la fois une méthode notifyObservers () et une méthode notifyObservers (Object arg) pour probablement spécifier les détails de ce qui a changé.
Dans mon cas, j'ai besoin de savoir si l'une des deux actions différentes s'est produite sur le sujet et, s'il s'agit d'une action particulière, de connaître un nombre entier lié à cette action.
Mes questions sont donc:
- quelle est la façon C ++ de passer un argument générique (comme le fait Java)?
- Observer est-il même le meilleur modèle? Peut-être une sorte de système d'événements?
MISE À JOUR
J'ai trouvé cet article qui parle de modèle du modèle Observer: Implémentation d'un modèle Subject / Observer avec des modèles . Cela m'a fait me demander si vous pouviez mettre en forme un argument.
J'ai trouvé cette question de débordement de pile qui parle de modeler l'argument: Modèle d'observateur de sujet basé sur un modèle - Dois-je utiliser static_cast ou dynamic_cast . Cependant, le PO semble avoir un problème auquel personne n'a répondu.
L'autre chose que je pourrais faire est de changer la méthode Update pour prendre un objet EventArg comme dans:
void MyClass::Update(Subject *subject, EventArg arg)
{
...
Ensuite, créez des sous-classes de EventArg pour des données d'argument spécifiques, puis je suppose que vous les restituez à la sous-classe spécifique dans la méthode de mise à jour.
MISE À JOUR 2
A également trouvé un article, sur la création d'un framework c ++ asynchrone basé sur les messages; la partie 2 qui explique comment le sujet doit communiquer des détails sur ce qui a changé.
J'envisage maintenant sérieusement d'utiliser Boost.Signals . Utiliser mon propre modèle d'observateur était logique quand c'était simple, mais le modèle et le type d'argument commencent à se compliquer. Et j'ai peut-être besoin de la sécurité des threads de Boost.Signals2.
MISE À JOUR 3
J'ai également trouvé quelques articles intéressants sur le modèle d'observateur:
Observateur généralisant par Herb Sutter
Implémentation du modèle d'observateur en C ++ - Partie 1
Expériences de mise en œuvre du modèle de conception d'observateur (partie 2)
Expériences de mise en œuvre du modèle de conception d'observateur (partie 3)
Cependant, j'ai changé mon implémentation pour utiliser Boost.Signals qui, bien que probablement un peu gonflé à mes fins, fonctionne correctement. Et probablement aucune préoccupation de ballonnement ou de vitesse n'est pertinente.
la source
Réponses:
Que ce soit C ++ ou JAVA, une notification à l'observateur peut accompagner les informations de ce qui a changé. Les mêmes méthodes notifyObservers (Object arg) peuvent également être utilisées en C ++.
Généralement, le problème restera est qu'il pourrait y avoir plusieurs sujets envoyés à un ou plusieurs observateurs et, par conséquent,
class arg
ils ne peuvent pas être codés en dur.Habituellement, la meilleure façon de faire est de créer arg sous la forme de messages / jetons génériques qui forment le même type de données pour différentes classes mais les valeurs diffèrent pour les différentes classes observées. Alternativement, si toutes ces valeurs de notification sont dérivées de la classe sur une classe basée qui est commune à tous.
Pour le modèle d'observateur, il est important que le type de données Arg ne soit pas codé en dur entre l'observé et l'observateur - sinon c'est un couplage qui rend les choses difficiles à évoluer.
MODIFIER
Si vous voulez que cet observateur non seulement observe, mais doit également effectuer beaucoup d'autres tâches en fonction de ce qui a changé , vous pouvez également consulter le modèle de visiteur . Dans le modèle de visiteur, l'observateur / visiteur appelle l' objet modifié et peut donc non seulement savoir ce qu'est la modification, mais peut réellement y travailler
la source
Object
(void *
,boost::any
ou quelque chose de la même générique) que si vous passez type spécifique, car avec le type spécifique que vous verrez au moment de la compilation que quelque chose a changé, alors que le type générique , il compilera et cessera de fonctionner, car l'observateur ne pourra pas travailler avec les données réelles transmises.std::function
Boost C ++ 11boost::function
, Gtk +GClosure
, méthodes liées à python, etc.), donc je définis simplement des méthodes avec les signatures appropriées et je demande au système de créer le réel observateur. Le couplage n'est en effet qu'une façon, le sujet définit l'interface pour les observateurs, mais n'a aucune idée de leur mise en œuvre.Il existe plusieurs façons d'envoyer un argument d'événement générique "Java Like" en C ++.
1) Déclarez l'argument d'événement comme void * et convertissez-le dans la bonne classe dans le gestionnaire d'événements.
2) Déclarez l'argument d'événement en tant que pointeur / référence à une nouvelle classe / interface telle que (en réponse à votre exemple)
Et que les classes d'arguments d'événement réels dérivent de cette classe.
Quant à votre question concernant un modèle basé sur l'observateur, consultez
http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ
la source
Je ne sais pas si c'est le nom canonique, mais depuis mes vieux jours Smalltalk je me souviens du terme "aspect" pour identifier ce qui a changé sur l'observable.
Ce n'est pas aussi complexe que votre idée d'EventArg (et de le sous-classer); il passe juste une chaîne (pourrait même être une constante entière) de l'observable à l'observateur.
Le plus: il n'y a que deux méthodes simples (
update(observable)
etupdateAspect(observable, aspect)
Moins: L'observateur devra peut-être demander à l'observable des informations supplémentaires (c'est-à-dire votre "nombre entier")
la source