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();
}

8 réponses

1 2 3
Avatar
Sylvain
Michael DOUBEZ wrote on 30/07/2007 13:34:

[...]
Si l'objet a un diagramme d'état (ce qui est souvent le cas avec
l'observer), c'est encore plus facile, je transmet l'id de la transition
causant la notification.


ce qui a été énoncé s'inspirait d'évènements asynchrones; si les msg de
notification relevait de changement d'une machine à état, d'autres
précautions devraient être prises (réentrance de "notify" par exemple).

Sylvain.

Avatar
Sylvain
David Fleury wrote on 30/07/2007 20:50:
Bonsoir,

voici à quoi j'arrive finalement
qui ressemble assez bien à ce que je voulais au détail
près que ça ne compile pas sous VS2005
(la fonction notify et une conversion IObservable<Observer>
vers Canard ou Chat)
alors que ça passe bien sous gcc 3.4 et comeau


en effet cela ne passe sous VC ... mais n'étant famillier de ces
templates je m'interroge sur cette écriture.

votre code ressemble assez à l'exemple de mem_fun_t donné par sgi [1],
or cette page indique que la fonction membre paramètre n'attends aucun
paramètre et votre "notified" reçoit un 'const Observable*'.
est-ce une source possible de l'erreur ?

Sylvain.

[1] <http://www.sgi.com/tech/stl/mem_fun_t.html>

Avatar
David Fleury
David Fleury wrote on 30/07/2007 20:50:
Bonsoir,

voici à quoi j'arrive finalement
qui ressemble assez bien à ce que je voulais au détail
près que ça ne compile pas sous VS2005
(la fonction notify et une conversion IObservable<Observer>
vers Canard ou Chat)
alors que ça passe bien sous gcc 3.4 et comeau


en effet cela ne passe sous VC ... mais n'étant famillier de ces
templates je m'interroge sur cette écriture.

votre code ressemble assez à l'exemple de mem_fun_t donné par sgi [1],
or cette page indique que la fonction membre paramètre n'attends aucun
paramètre et votre "notified" reçoit un 'const Observable*'.
est-ce une source possible de l'erreur ?

Sylvain.

[1] <http://www.sgi.com/tech/stl/mem_fun_t.html>


J'ai fait pas mal d'essai (création d'un binder2nd, ...) sans succès.
Je crois qu'on va partir sur une base plus simple (enum, plus valeur)
ça devrait passer plus facilement sur les 2 ou 3 compilateurs que nous
allons rencontrer lors de nos portages.
tant pis, cette solution me plaisait bien.

les exemples présents sur sgi fonctionnent bien sous VS2005, même avec
le bind2nd, j'ai l'impression que c'est le template utilisé (sous forme
de CRTP) qui pose un problème à VS. Je ferais sûrement d'autres essais
pour voir où se trouve la limite et/ou peut être mon erreur.

David


Avatar
Michael DOUBEZ
David Fleury wrote on 30/07/2007 20:50:
Bonsoir,

voici à quoi j'arrive finalement
qui ressemble assez bien à ce que je voulais au détail
près que ça ne compile pas sous VS2005
(la fonction notify et une conversion IObservable<Observer>
vers Canard ou Chat)
alors que ça passe bien sous gcc 3.4 et comeau


en effet cela ne passe sous VC ... mais n'étant famillier de ces
templates je m'interroge sur cette écriture.

votre code ressemble assez à l'exemple de mem_fun_t donné par sgi [1],
or cette page indique que la fonction membre paramètre n'attends aucun
paramètre et votre "notified" reçoit un 'const Observable*'.
est-ce une source possible de l'erreur ?


Je m'interroge sur le moment de l'instanciation des fonctions membres
dans IObservable<Observable>::notify().

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

Dans la mesure où Observer<Observable>::notified() n'est pas instancié
au moment de la déclaration des observable, je me demande si il n'y
aurait pas quelque chose la dessous.

Michael


Avatar
Michael DOUBEZ
David Fleury wrote on 30/07/2007 20:50:
Bonsoir,

voici à quoi j'arrive finalement
qui ressemble assez bien à ce que je voulais au détail
près que ça ne compile pas sous VS2005
(la fonction notify et une conversion IObservable<Observer>
vers Canard ou Chat)
alors que ça passe bien sous gcc 3.4 et comeau


en effet cela ne passe sous VC ... mais n'étant famillier de ces
templates je m'interroge sur cette écriture.

votre code ressemble assez à l'exemple de mem_fun_t donné par sgi [1],
or cette page indique que la fonction membre paramètre n'attends aucun
paramètre et votre "notified" reçoit un 'const Observable*'.
est-ce une source possible de l'erreur ?


Je m'interroge sur le moment de l'instanciation des fonctions membres
dans IObservable<Observable>::notify().

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

Dans la mesure où Observer<Observable>::notified() n'est pas instancié
au moment de la déclaration des observable, je me demande si il n'y
aurait pas quelque chose la dessous.


En deuxième lecture, je pense qui vaudrait la coup d'utiliser une
fonction static de classe dans IObserver<Observable> qui appelerait
IObserver<Observable>::notified() - qui peut devenir protected si tu ne
veux pas l'exposer.

template< typename Observable >
struct IObserver
{
virtual ~IObserver() {};
virtual void notified( const Observable* ) = 0;

static void notifyObserver(IObserver* observer, Observable* observable)
{
observer->notified(observable);
}
};

Puis

void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( std::ptr_fun(
&IObserver<Observable>::notifyObserver ),
this ) );
}

Michael



Avatar
Michael DOUBEZ
David Fleury wrote on 30/07/2007 20:50:
Bonsoir,

voici à quoi j'arrive finalement
qui ressemble assez bien à ce que je voulais au détail
près que ça ne compile pas sous VS2005
(la fonction notify et une conversion IObservable<Observer>
vers Canard ou Chat)
alors que ça passe bien sous gcc 3.4 et comeau


en effet cela ne passe sous VC ... mais n'étant famillier de ces
templates je m'interroge sur cette écriture.

votre code ressemble assez à l'exemple de mem_fun_t donné par sgi
[1], or cette page indique que la fonction membre paramètre n'attends
aucun paramètre et votre "notified" reçoit un 'const Observable*'.
est-ce une source possible de l'erreur ?


Je m'interroge sur le moment de l'instanciation des fonctions membres
dans IObservable<Observable>::notify().

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

Dans la mesure où Observer<Observable>::notified() n'est pas instancié
au moment de la déclaration des observable, je me demande si il n'y
aurait pas quelque chose la dessous.


En deuxième lecture, je pense qui vaudrait la coup d'utiliser une
fonction static de classe dans IObserver<Observable> qui appelerait
IObserver<Observable>::notified() - qui peut devenir protected si tu ne
veux pas l'exposer.

template< typename Observable >
struct IObserver
{
virtual ~IObserver() {};
virtual void notified( const Observable* ) = 0;

static void notifyObserver(IObserver* observer, Observable* observable)
{
observer->notified(observable);
}
};

Puis

void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( std::ptr_fun(
&IObserver<Observable>::notifyObserver ),
this ) );
}


Finalement, j'ai reussis sous gcc3.4 et VC6 en utilisant un functor.
Ca a l'air bizarre mais je pense que VC6 n'instancie pas correctement (
peut être est ce du au fait que la 1ere instanciation de
Observer<Observable> est faite dans une classe).


//--------------------------------------------------------------------------
template< typename Observable >
struct IObserver
{
virtual ~IObserver() {};
virtual void notified( const Observable* ) = 0;
};

//--------------------------------------------------------------------------
// result_type=int car VC6 ne suppore pas une valeur de retour void
template< typename Observable >
struct notifyObserver: public binary_function<IObserver<Observable>*,
Observable*,int>
{
int operator()(IObserver<Observable>* observer, Observable* observable)
const
{
observer->notified(observable);
return 0;
}
};


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

protected:
void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( notifyObserver<Observable>(),
this ) );
}
};

Bon WE.

Michael




Avatar
David Fleury
David Fleury wrote on 30/07/2007 20:50:
Bonsoir,

voici à quoi j'arrive finalement
qui ressemble assez bien à ce que je voulais au détail
près que ça ne compile pas sous VS2005
(la fonction notify et une conversion IObservable<Observer>
vers Canard ou Chat)
alors que ça passe bien sous gcc 3.4 et comeau


en effet cela ne passe sous VC ... mais n'étant famillier de ces
templates je m'interroge sur cette écriture.

votre code ressemble assez à l'exemple de mem_fun_t donné par sgi
[1], or cette page indique que la fonction membre paramètre
n'attends aucun paramètre et votre "notified" reçoit un 'const
Observable*'.
est-ce une source possible de l'erreur ?


Je m'interroge sur le moment de l'instanciation des fonctions membres
dans IObservable<Observable>::notify().

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

Dans la mesure où Observer<Observable>::notified() n'est pas
instancié au moment de la déclaration des observable, je me demande
si il n'y aurait pas quelque chose la dessous.


En deuxième lecture, je pense qui vaudrait la coup d'utiliser une
fonction static de classe dans IObserver<Observable> qui appelerait
IObserver<Observable>::notified() - qui peut devenir protected si tu
ne veux pas l'exposer.

template< typename Observable >
struct IObserver
{
virtual ~IObserver() {};
virtual void notified( const Observable* ) = 0;

static void notifyObserver(IObserver* observer, Observable* observable)
{
observer->notified(observable);
}
};

Puis

void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( std::ptr_fun(
&IObserver<Observable>::notifyObserver ),
this ) );
}


Finalement, j'ai reussis sous gcc3.4 et VC6 en utilisant un functor.
Ca a l'air bizarre mais je pense que VC6 n'instancie pas correctement (
peut être est ce du au fait que la 1ere instanciation de
Observer<Observable> est faite dans une classe).


//--------------------------------------------------------------------------

template< typename Observable >
struct IObserver
{
virtual ~IObserver() {};
virtual void notified( const Observable* ) = 0;
};

//--------------------------------------------------------------------------

// result_type=int car VC6 ne suppore pas une valeur de retour void
template< typename Observable >
struct notifyObserver: public binary_function<IObserver<Observable>*,
Observable*,int>
{
int operator()(IObserver<Observable>* observer, Observable* observable)
const
{
observer->notified(observable);
return 0;
}
};


//---------------------------------------------------------------------------

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

protected:
void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( notifyObserver<Observable>(),
this ) );
}
};

Bon WE.

Michael


Merci
j'essayerai cette solution lundi.

David





Avatar
David Fleury
David Fleury wrote on 30/07/2007 20:50:
Bonsoir,

voici à quoi j'arrive finalement
qui ressemble assez bien à ce que je voulais au détail
près que ça ne compile pas sous VS2005
(la fonction notify et une conversion IObservable<Observer>
vers Canard ou Chat)
alors que ça passe bien sous gcc 3.4 et comeau


en effet cela ne passe sous VC ... mais n'étant famillier de ces
templates je m'interroge sur cette écriture.

votre code ressemble assez à l'exemple de mem_fun_t donné par sgi
[1], or cette page indique que la fonction membre paramètre
n'attends aucun paramètre et votre "notified" reçoit un 'const
Observable*'.
est-ce une source possible de l'erreur ?


Je m'interroge sur le moment de l'instanciation des fonctions membres
dans IObservable<Observable>::notify().

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

Dans la mesure où Observer<Observable>::notified() n'est pas
instancié au moment de la déclaration des observable, je me demande
si il n'y aurait pas quelque chose la dessous.


En deuxième lecture, je pense qui vaudrait la coup d'utiliser une
fonction static de classe dans IObserver<Observable> qui appelerait
IObserver<Observable>::notified() - qui peut devenir protected si tu
ne veux pas l'exposer.

template< typename Observable >
struct IObserver
{
virtual ~IObserver() {};
virtual void notified( const Observable* ) = 0;

static void notifyObserver(IObserver* observer, Observable* observable)
{
observer->notified(observable);
}
};

Puis

void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( std::ptr_fun(
&IObserver<Observable>::notifyObserver ),
this ) );
}


Finalement, j'ai reussis sous gcc3.4 et VC6 en utilisant un functor.
Ca a l'air bizarre mais je pense que VC6 n'instancie pas correctement (
peut être est ce du au fait que la 1ere instanciation de
Observer<Observable> est faite dans une classe).


//--------------------------------------------------------------------------

template< typename Observable >
struct IObserver
{
virtual ~IObserver() {};
virtual void notified( const Observable* ) = 0;
};

//--------------------------------------------------------------------------

// result_type=int car VC6 ne suppore pas une valeur de retour void
template< typename Observable >
struct notifyObserver: public binary_function<IObserver<Observable>*,
Observable*,int>
{
int operator()(IObserver<Observable>* observer, Observable* observable)
const
{
observer->notified(observable);
return 0;
}
};


//---------------------------------------------------------------------------

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

protected:
void notify() const {
for_each( observers_.begin(),
observers_.end(),
std::bind2nd( notifyObserver<Observable>(),
this ) );
}
};

Bon WE.

Michael


Et bien, ça n'a pas l'air de fonctionner avec VS2003 en tout cas.
Je crois qu'il manque un const dans sur Observable pour le functor.
Mais ça ne change pas grand chose au résultat.

David





1 2 3