2010-12-03 9 views
2

Je me réfère à l'article Implementing a Subject/Observer pattern with templatesmodèle à base de modèle Objet d'observateur - Dois-je utiliser static_cast ou dynamic_cast

Je l'ai fait quelques modifications et il est devenu le code comme suit.

template <class T, class A> 
class Observer { 
public: 
    Observer() {} 
    virtual ~Observer() {} 
    virtual void update(T& subject, A arg) = 0; 
}; 

template <class T, class A> 
class Subject 
{ 
public: 
    Subject() {} 
    virtual ~Subject() {} 

    // Take note that, we didn't make the following functions as virtual, 
    // as we do not expect them to be overridden. 
    void attach(Observer<T, A> &observer) { 
     // Ensure no duplication. 
     std::vector<Observer<T, A> *>::const_iterator iterator = std::find(observers.begin(), observers.end(), &observer); 
     if (iterator == observers.end()) { 
      observers.push_back(&observer); 
     } 
    } 

    void dettach(Observer<T, A> &observer) { 
     std::vector<Observer<T, A> *>::const_iterator iterator = std::find(observers.begin(), observers.end(), &observer); 
     if (iterator != observers.end()) { 
      observers.erase(iterator); 
     } 
    } 

    void dettachAll() { 
     observers.clear(); 
    } 

    void notify(A arg) 
    { 
     std::vector<Observer<T, A> *>::const_iterator it; 
     for (it = observers.begin(); it != observers.end(); it++) { 
      (*it)->update(*(static_cast<T *>(this)), arg); 
     } 
    } 

private: 
    std::vector<Observer<T, A> *> observers; 
}; 

Plus tard, je me rends compte que (*it)->update(*(static_cast<T *>(this)), arg); a une limitation. Par exemple,

// cause compilation error in static_cast, as it cannot cast cat1 to animal. 
class cat1 : public animal, public Subject<animal, int> { 
public: 
    virtual void speak() { 
     notify(888); 
    } 
}; 

class zoo1 : public Observer<animal, int> { 
public: 
    zoo1() { 
     c.attach(*this); 
     c.speak(); 
    } 

    virtual void update(animal& subject, int arg) { 
     cout << "zoo1 received notification " << arg << endl; 
    } 

    cat1 c; 
}; 

je peux résoudre le problème en changeant le static_cast à dynamic_cast. Cependant, je ne suis pas sûr de tomber dans d'autres pièges? Ma conjecture sur l'intention originale de l'auteur en ayant static_cast, est d'assurer la vérification de sécurité de type lors de la compilation.

+1

Cela semble être répondu ici: [Dois-je utiliser la distribution dynamique dans le modèle d'observateur de sujet avec des modèles] (http://stackoverflow.com/questions/3975624/should-i-use-dynamic-cast-in-the -subject-observer-pattern-with-templates) – User

Répondre

2

Votre problème vient du fait que l'animal doit faire l'objet non seulement le chat,

class animal : public Subject<animal,int> 
{ 
    ... 
}; 

class cat1 : public animal 
{ 
    public: 
    virtual void speak() 
    { 
     notify(888); 
    } 
}; 

class zoo1 : public Observer<animal, int> { 
public: 
    zoo1() 
    { 
     c.attach(*this); 
     c.speak(); 
    } 

virtual void update(animal& subject, int arg) 
    { 
     cout << "zoo1 received notification " << arg << endl; 
    } 

cat1 c; 
}; 

En faisant cela, chaque sujet sont « en mesure » statique moulé sous pression à l'animal. Ce n'est pas le cas avec votre cat1

0

Comme l'a souligné Cyrs, dans votre exemple, le compilateur essaie de static_cast un Subject<animal, int>* dans un animal* alors que ni animal ni Subject<animal, int> hérite l'autre, même indirectement. Le compilateur considère cette conversion comme impossible.

Si vous remplacez static_cast par dynamic_cast, il sera évalué au moment de l'exécution si cette conversion est possible. Dans votre exemple, il est, car cat1 est heureusement à la fois un animal et un Observer<animal, int>. Donc, cela fonctionnera toujours avec dynamic_cast si l'utilisateur (développeur) ne fait aucune erreur, c'est-à-dire s'il/elle fournit des classes avec ce type de hiérarchie.

Ainsi, vous souhaiterez peut-être rendre votre implémentation avec dynamic_cast plus robuste et détecter les erreurs de programmation. Avec une telle erreur, dynamic_cast retournera un pointeur nul. Vous pouvez donc tester si dynamic_cast renvoie un pointeur nul soit dans notify(), dans attach() ou dans Subject(), et évitez d'utiliser le pointeur dans ce cas.

Notez que dynamic_cast est moins efficace que static_cast en raison des évaluations de type à l'exécution.

Je préfère opter pour la solution de Cyrs.

Comme souligné dans un commentaire, vous devriez également considérer la réponse acceptée du fil suivant: Should I use dynamic cast in the subject observer pattern with templates, ce qui est une bonne alternative.