Publisher/Subscriber Flashcards
Dans quel contexte le patron “observateur-observé” devient-il difficile à gérer?
Le patron “observateur-observé” devient difficile à gérer lorsque les observateurs varient fréquemment en nombre. Dans ce cas, il devient rapidement complexe pour les sujets de maintenir leurs listes d’observateurs à jour.
Pourquoi la gestion des abonnements observateurs-observés peut-elle devenir problématique dans un jeu de grande envergure?
Dans les jeux de grande envergure en continu (sans chargement de scènes ou de niveaux), il n’est pas possible de charger tous les acteurs au début. Par conséquent, gérer les abonnements entre observateurs et sujets peut devenir extrêmement complexe.
Quelle est la principale différence entre le patron “observateur-observé” et le patron “publish-subscribe”?
Dans le patron “publish-subscribe”, un observateur (subscriber) ne s’abonne plus directement à un sujet, mais plutôt à un événement. Les sujets notifient un publisher qu’un événement s’est produit, et ce publisher relaie l’information à tous les subscribers abonnés à cet événement.
Dans le patron “publish-subscribe”, à quoi s’abonne un subscriber?
Un subscriber s’abonne à un événement spécifique, et non à un sujet comme dans le patron “observateur-observé”.
Donnez un exemple concret illustrant le fonctionnement du patron “publish-subscribe”.
Dans le domaine de la musique, les fans (subscribers) peuvent s’abonner aux nouvelles de leurs groupes préférés. Lorsqu’une nouvelle est publiée (par le groupe ou un festival), le publisher envoie cette information à tous les fans abonnés aux nouvelles de ce groupe.
Comment définit-on généralement les événements dans une implémentation du patron “publish-subscribe”?
Les événements sont généralement définis à l’aide d’une énumération (enum), par exemple:
enum class Event {
VALUE_CHANGED,
BOMB_EXPLODED,
OTHER_EVENT,
// etc
};
Quelles sont les principales méthodes de la classe Publisher?
Les principales méthodes de la classe Publisher sont:
- addSubscriber(Subscriber& o, Event event):
pour ajouter un subscriber à un événement - removeSubscriber(Subscriber& o, Event event):
pour retirer un subscriber d’un événement - notifySubscribers(Event event, const void* data):
pour notifier tous les subscribers d’un événement spécifique
Quelle structure de données est appropriée pour stocker les subscribers dans la classe Publisher et pourquoi?
Un std::multimap<Event, std::reference_wrapper<Subscriber>> est approprié car il permet de regrouper plusieurs "valeurs" (les subscribers) pour une seule "clé" (un Event). Cela facilite la recherche des subscribers par événement.</Subscriber>
Pourquoi le Publisher stocke-t-il des références aux subscribers plutôt que des pointeurs?
Il serait incohérent de permettre l’ajout de pointeurs directement alloués avec new, car les subscribers sont des acteurs qui ont d’autres rôles dans le jeu, ils ne font pas seulement observer. Ils existent indépendamment du système de notification.
Quel type de paramètre est utilisé dans la méthode notifySubscribers pour transmettre des données avec l’événement?
La méthode utilise un paramètre de type const void*, ce qui permet de passer n’importe quel type d’information (chaînes de caractères, nombres, objets, etc.).
Comment un acteur peut-il notifier les subscribers qu’un événement s’est produit?
Un acteur peut notifier les subscribers en appelant la méthode notifySubscribers du publisher, en spécifiant l’événement et en fournissant éventuellement des données supplémentaires:
publisher.notifySubscribers(Event::VALUE_CHANGED, “any data here”);
Donnez deux exemples de données qui peuvent être passées lors de la notification d’un événement.
Une chaîne de caractères: publisher.notifySubscribers(Event::VALUE_CHANGED, “any data here”);
Un nombre: publisher.notifySubscribers(Event::OTHER_EVENT, (void*)123);
Comment un subscriber peut-il interpréter correctement les données reçues lors d’une notification?
Comme seuls les subscribers abonnés à un événement spécifique reçoivent la notification, ils peuvent facilement transtyper le paramètre dans le bon type sans avoir à tester préalablement le sujet:
void ConcreteSubscriber::notify(Event event, const void* data) {
switch (event) {
case Event::VALUE_CHANGED:
std::cout «_space;“Valeur de la chaine: “ «_space;(char*)data «_space;std::endl;
break;
// …
}
}
Quel est l’avantage principal du patron “publish-subscribe” par rapport au patron “observateur-observé”?
L’avantage principal est le découplage entre les émetteurs d’événements et les récepteurs. Les subscribers ne connaissent pas les sources des événements, et les émetteurs ne savent pas qui recevra leurs notifications, ce qui simplifie grandement la gestion des abonnements dans des systèmes dynamiques.
Comment un acteur peut-il se passer lui-même comme donnée lors de la notification d’un événement?
Un acteur peut se passer lui-même en utilisant le pointeur this:
publisher.notifySubscribers(Event::ACTOR_CHANGED, this);
Pourquoi est-il important que les subscribers connaissent à l’avance le type de données associé à chaque événement?
Comme les données sont transmises sous forme de void*, les subscribers doivent connaître le type correct pour effectuer la conversion appropriée. Sans cette connaissance, ils risqueraient d’interpréter incorrectement les données, ce qui pourrait causer des comportements inattendus ou des plantages.
En quoi le patron “publish-subscribe” facilite-t-il la gestion des événements dans un jeu où les acteurs sont créés et détruits dynamiquement?
Comme les abonnements se font aux événements et non aux acteurs, peu importe si un acteur est créé ou détruit, seuls les subscribers intéressés par un événement spécifique sont notifiés. Cela élimine la nécessité de maintenir des listes d’observateurs pour chaque sujet et simplifie grandement la gestion des abonnements.
Comment implémenter la méthode removeSubscriber dans la classe Publisher?
void Publisher::removeSubscriber(Subscriber& o, Event event) {
auto range = subscribers.equal_range(event);
for (auto it = range.first; it != range.second; ) {
if (&(it->second.get()) == &o) {
it = subscribers.erase(it);
} else {
++it;
}
}
}
Quelle serait une signature typique pour la méthode notify de la classe Subscriber?
virtual void notify(Event event, const void* data) = 0;
Cette méthode est généralement déclarée virtuelle pure dans une classe de base Subscriber, permettant aux classes dérivées d’implémenter leur propre logique de traitement des événements.
Quel est l’intérêt d’utiliser un multimap plutôt qu’une simple map pour stocker les subscribers?
Un multimap permet d’associer plusieurs valeurs (subscribers) à une même clé (événement), ce qui est essentiel puisque plusieurs subscribers peuvent être intéressés par le même événement. Une map simple ne permettrait d’associer qu’un seul subscriber à chaque événement.