Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Modèle Observateur

28 réponses
Avatar
David Fleury
Bonjour,
j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?
Dans le cas ou je peux avoir plusieurs observateurs et observés
concrets différents (comme ici, Canard, Chat, et Chasseur et naturaliste)

D'avoir quelque chose comme :
struct Naturaliste : IObserver {
void notified( const Canard* c ) {
cout << "I am The Naturaliste ["
<< this << "] notified by [" << (*c) << "]" << endl;
}
void notified( const Chat* c ) {
cout << "I am The Naturaliste ["
<< this << "] notified by [" << (*c) << "]" << endl;
}
};

ou peut être y a t-il une manière plus élégante de le faire
de manière générale.

David



// [CODE]
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
using namespace std;

struct IObservable;
ostream& operator << ( ostream& os, const IObservable& o );

// --------------------------------------------------------------------
struct IObserver {
virtual ~IObserver() {}
virtual void notified( const IObservable* o ) = 0;
};

// ---------------------------------------------------------------------
struct IObservable {
list<IObserver*> observers_;
void add( IObserver* o ) {
observers_.push_back( o );
}
void remove( IObserver* observer ) {
observers_.erase( std::remove( observers_.begin(),
observers_.end(),
observer ) );
}

virtual void display( ostream& os ) const = 0;

protected:
void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( std::mem_fun( &IObserver::notified ),
this ) );
}
};

ostream& operator << ( ostream& os, const IObservable& o )
{
o.display( os );
return os;
}

// ---------------------------------------------------------------------
struct Canard : IObservable {
void CoinCoin()
{
cout << "Coin Coin" << endl;
notify();
}

void display( ostream& os ) const { os << "The Canard"; }
};

// ---------------------------------------------------------------------
struct Chat : IObservable {
void MiaouMiaou()
{
cout << "Miaou Miaou" << endl;
notify();
}
void display( ostream& os ) const { os << "The Chat"; }
};

// ---------------------------------------------------------------------
struct Chasseur : IObserver {
void notified( const IObservable* c ) {
cout << "I am The Chasseur [" << this
<< "] notified by [" << (*c) << "]" << endl;
}
};

struct Naturaliste : IObserver {
void notified( const IObservable* c ) {
cout << "I am The Naturaliste ["
<< this << "] notified by [" << (*c) << "]" << endl;
}
};

// ---------------------------------------------------------------------
int main() {
Chasseur c1, c2;
Naturaliste n1;

Canard canard;
canard.add( &c1 );
canard.add( &c2 );
canard.add( &n1 );

canard.CoinCoin();

canard.remove( &c2 );

canard.CoinCoin();

Chat chat;
chat.add( &n1 );

chat.MiaouMiaou();
}

10 réponses

1 2 3
Avatar
Christophe Lephay
"David Fleury" a écrit dans le message de news:
46aa2d8f$0$27186$
Bonjour,
j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?
Dans le cas ou je peux avoir plusieurs observateurs et observés
concrets différents (comme ici, Canard, Chat, et Chasseur et naturaliste)

D'avoir quelque chose comme :
struct Naturaliste : IObserver {
void notified( const Canard* c ) {
cout << "I am The Naturaliste ["
<< this << "] notified by [" << (*c) << "]" << endl;
}
};
[...]

struct IObserver {
virtual ~IObserver() {}
virtual void notified( const IObservable* o ) = 0;
};

// ---------------------------------------------------------------------
struct IObservable {
list<IObserver*> observers_;
[...]


Tu peux, auquel cas tu dois remplacer list<IObserver*> par
list<Naturaliste*> pour appeler void Naturaliste::notified( const Canard
* ); dans ta fonction Canard::notify.

Ceci dit, tu as une raison particulière de vouloir le faire ?

Avatar
David Fleury
"David Fleury" a écrit dans le message de news:
46aa2d8f$0$27186$
Bonjour,
j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?
Dans le cas ou je peux avoir plusieurs observateurs et observés
concrets différents (comme ici, Canard, Chat, et Chasseur et naturaliste)

D'avoir quelque chose comme :
struct Naturaliste : IObserver {
void notified( const Canard* c ) {
cout << "I am The Naturaliste ["
<< this << "] notified by [" << (*c) << "]" << endl;
}
};
[...]

struct IObserver {
virtual ~IObserver() {}
virtual void notified( const IObservable* o ) = 0;
};

// ---------------------------------------------------------------------
struct IObservable {
list<IObserver*> observers_;
[...]


Tu peux, auquel cas tu dois remplacer list<IObserver*> par
list<Naturaliste*> pour appeler void Naturaliste::notified( const Canard
* ); dans ta fonction Canard::notify.

Ceci dit, tu as une raison particulière de vouloir le faire ?




Pour proposer une solution générale à ce problème sans avoir à le
recoder les interfaces pour chacun des observateurs/observés concrets.
J'ai peur sinon d'avoir à recoder le add/remove/notify autant de fois


Avatar
Christophe Lephay
"David Fleury" a écrit dans le message de news:
46aa36d2$0$27186$
"David Fleury" a écrit dans le message de news:
j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?
Ceci dit, tu as une raison particulière de vouloir le faire ?

Pour proposer une solution générale à ce problème sans avoir à le recoder

les interfaces pour chacun des observateurs/observés concrets.
J'ai peur sinon d'avoir à recoder le add/remove/notify autant de fois


Je suis pas sur de comprendre. La solution générale ne consiste-t-elle pas,
justement, à se restreindre à l'interface IObservable ?



Avatar
Sylvain
David Fleury wrote on 27/07/2007 19:38:

j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?


une interface Observable et donc une unique methode de notification:

void IObserver::notify(const Observable& notifier){ ... }

serait justement plus simple qu'une énumération, à la Prévert, de
méthodes notified à signature distincte.

Dans le cas ou je peux avoir plusieurs observateurs et observés
concrets différents (comme ici, Canard, Chat, et Chasseur et naturaliste)

struct Naturaliste : IObserver {
void notified( const Canard* c ) {
cout << " " << this << " " << (*c) << endl;
}
void notified( const Chat* c ) {
cout << " " << this << " " << (*c) << endl;
}
};


on voit ici que le type exact de l'observé-paramètre n'a aucun intérêt
(n'a nul besoin d'être connu), ce type paramètre a seulement besoin
d'être injectable.

ou peut être y a t-il une manière plus élégante de le faire
de manière générale.
[...]

int main() {
Chasseur c1, c2;
Naturaliste n1;

Canard canard;
canard.add( &c1 );
canard.add( &c2 );
canard.add( &n1 );
}


il existe toujours d'autres manières de faire, l'élégance reste par
contre question de sensibilité ... et de finalité.

ce que je trouve byzarre ici est le retournement du schéma classique;
habituellement on définit des listeneurs d'évènements et ceux-ci gèrent
une liste de références de générateurs d'évènements; donc un chasseur ou
un naturaliste listerait le canard, le chat, ..., qu'il voudrait
observer; cela ne changerait toutefois pas l'intérêt de définir des
interfaces (ou votre envie de multiplier les méthodes).

Sylvain.

Avatar
James Kanze
On Jul 27, 8:17 pm, David Fleury wrote:
"David Fleury" a écrit dans le message de n ews:
46aa2d8f$0$27186$
Bonjour,
j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?
Dans le cas ou je peux avoir plusieurs observateurs et observés
concrets différents (comme ici, Canard, Chat, et Chasseur et natural iste)

D'avoir quelque chose comme :
struct Naturaliste : IObserver {
void notified( const Canard* c ) {
cout << "I am The Naturaliste ["
<< this << "] notified by [" << (*c) << "]" << endl;
}
};
[...]

struct IObserver {
virtual ~IObserver() {}
virtual void notified( const IObservable* o ) = 0;
};

// ------------------------------------------------------------------- --
struct IObservable {
list<IObserver*> observers_;
[...]


Tu peux, auquel cas tu dois remplacer list<IObserver*> par
list<Naturaliste*> pour appeler void Naturaliste::notified( const Canard
* ); dans ta fonction Canard::notify.

Ceci dit, tu as une raison particulière de vouloir le faire ?


Pour proposer une solution générale à ce problème sans avoir à
le recoder les interfaces pour chacun des
observateurs/observés concrets. J'ai peur sinon d'avoir à
recoder le add/remove/notify autant de fois


Je ne suis pas trop sûr de ton problème, mais si tu veux un
Observer générique sans risques de typage (comment dit-on
« type safe » en français ?), est-ce que les templates ne
feraient pas l'affaire :

template< typename Observed >
class Observer
{
virtual ~Observer() {}
virtual void notify() = 0 ;
} ;

(Mais en général, la fonction notify() va prendre des
paramètres, dont le type et nombre dépendront de Observed. Je ne
sais pas donc si c'est une solution valable.)

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
James Kanze
On Jul 27, 11:34 pm, Sylvain wrote:
David Fleury wrote on 27/07/2007 19:38:
j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?


une interface Observable et donc une unique methode de notification:

void IObserver::notify(const Observable& notifier){ ... }

serait justement plus simple qu'une énumération, à la Prévert, de
méthodes notified à signature distincte.


Sauf qu'en général, je crois, notify() prendrait plutôt des
paramètres de type plus spécifique, non ? Du genre Event.
(Enfin, c'est comme ça que je l'ai toujours implémenté.)

[...]
ce que je trouve byzarre ici est le retournement du schéma classique;
habituellement on définit des listeneurs d'évènements et ceux-ci g èrent
une liste de références de générateurs d'évènements; donc un chasseur ou
un naturaliste listerait le canard, le chat, ..., qu'il voudrait
observer; cela ne changerait toutefois pas l'intérêt de définir des
interfaces (ou votre envie de multiplier les méthodes).


Il faut bien que le générateur d'évenemment sache trouver les
listeneurs. La plupart du temps, c'est une relation
bidirectionnelle : le listeneur doit bien connaît le
générateur, afin de s'y intéresser, mais le générateur doit
connaître aussi les listeneurs, afin de savoir où envoyer les
évenemments. (Typiquement, le listeneur connaît le générateur
directement, plus ou moins, tandis que le générateur ne connaît
les listeneurs que parce qu'ils s'y sont inscrits. C'est
peut-être ce que tu voulais dire, et que j'ai mal compris.)

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Sylvain
James Kanze wrote on 28/07/2007 11:17:

void IObserver::notify(const Observable& notifier){ ... }

serait justement plus simple qu'une énumération, à la Prévert, de
méthodes notified à signature distincte.


Sauf qu'en général, je crois, notify() prendrait plutôt des
paramètres de type plus spécifique, non ? Du genre Event.
(Enfin, c'est comme ça que je l'ai toujours implémenté.)


je l'utilise comme ça également - le listener n'a (souvent) pas à
connaitre une classe "générateur d'évènements" mais seulement la classe
évènement.
j'ai gardé l'esprit du PO en transmettant ce générateur, mais je ne
l'implémenterais pas non plus comme cela.

[...]
(Typiquement, le listeneur connaît le générateur
directement, plus ou moins, tandis que le générateur ne connaît
les listeneurs que parce qu'ils s'y sont inscrits. C'est
peut-être ce que tu voulais dire, et que j'ai mal compris.)


je suis d'accord, et je l'ai mal exprimé.

ce que tu décris est le modèle habituel et en fait le PO reprends bien
ce modèle.

les exemples du PO, par ses noms de classes me faisait penser à une
retournement dans le sens où, j'imagine il y a ici plus de générateurs
que de listeneurs.

or, pour un schéma classique, on a souvent peu de générateurs (peu
d'instances d'une même classe) et possiblement de nombreux listeners,
d'où un classique "addListener" (public) fourni par le générateur.

dans le cas, où 1 ou 2 chasseurs / naturalistes écouteraient un groupe
de canards, j'aurais tendance à définir un "addObservable()" dans cette
classe listener - et cette méthode utiliserait un service ami du
générateur pour s'enregistrer, ceci donnant, imho, une meilleure vision
des priorités / responsabilités des agents.

Sylvain.


Avatar
David Fleury
"David Fleury" a écrit dans le message de news:
46aa36d2$0$27186$
"David Fleury" a écrit dans le message de news:
j'essaye d'implémenter un modèle observateur, j'arrive
à ce qui suit mais est-il possible d'avoir au niveau
de l'observateur concret une méthode ayant pour signature le
type exact de l'observé et non pas l'interface IObservable ?
Ceci dit, tu as une raison particulière de vouloir le faire ?

Pour proposer une solution générale à ce problème sans avoir à le recoder

les interfaces pour chacun des observateurs/observés concrets.
J'ai peur sinon d'avoir à recoder le add/remove/notify autant de fois


Je suis pas sur de comprendre. La solution générale ne consiste-t-elle pas,
justement, à se restreindre à l'interface IObservable ?




Dans ce cas, dans la mesure où restreindre l'interface me donne plus de
travail, j'assimile plus ça à une spécialisation. Une généralisation du
problème devrait me faire mon travailler.




Avatar
David Fleury
On Jul 27, 8:17 pm, David Fleury wrote:


Pour proposer une solution générale à ce problème sans avoir à
le recoder les interfaces pour chacun des
observateurs/observés concrets. J'ai peur sinon d'avoir à
recoder le add/remove/notify autant de fois


Je ne suis pas trop sûr de ton problème, mais si tu veux un
Observer générique sans risques de typage (comment dit-on
« type safe » en français ?), est-ce que les templates ne
feraient pas l'affaire :

template< typename Observed >
class Observer
{
virtual ~Observer() {}
virtual void notify() = 0 ;
} ;

(Mais en général, la fonction notify() va prendre des
paramètres, dont le type et nombre dépendront de Observed. Je ne
sais pas donc si c'est une solution valable.)



J'ai rapidement essayé de faire une solution template aussi bien pour
l'Observer et l'Observed mais je ne m'en suis pas sorti.

Dans mon cas concret, j'aurais plusieurs Observed pour un type Observer
donc je peux réessayer en template dans cette direction.
Je crois qu'une de mes versions template fonctionnait pour l'Observer en
template utilisé par le notify mais c'était sans grand intéret.


Avatar
Christophe Lephay
"David Fleury" a écrit dans le message de news:
46aba941$0$10323$
Je suis pas sur de comprendre. La solution générale ne consiste-t-elle
pas, justement, à se restreindre à l'interface IObservable ?


Dans ce cas, dans la mesure où restreindre l'interface me donne plus de
travail, j'assimile plus ça à une spécialisation. Une généralisation du
problème devrait me faire mon travailler.


Eun non. Te restreindre à l'interface IObservable, ça veut dire faire avec
l'existant sans rien ajouter.


1 2 3